hashman.cahttps://hashman.ca/2021-06-17T11:20:00-04:00I'm hosting a Bug Scrub for Kubernetes SIG Node2021-06-17T11:20:00-04:002021-06-17T11:20:00-04:00Elana Hashmantag:hashman.ca,2021-06-17:/sig-node-bsp/<p>I'm hosting a Kubernetes SIG Node bug scrub on Thursday, June 24th through Friday, June 25th, kicking off at 00:00 UTC on the 24th.</p><p>It's been a long while since I <a href="/nyc-bsp">last hosted a BSP</a>, but 'tis the season.</p>
<p>Kubernetes SIG Node will be holding a bug scrub on June 24-25, and this is a
great opportunity for you to get involved if you're interested in contributing
to Kubernetes or SIG Node!</p>
<p>We will be hosting a global event with region captains for all timezones. I am
one of the NASA captains (~17:00-01:00 UTC) and I'll be leading the kickoff. We
will be working on Slack and Zoom. I hope you'll be able to drop in!</p>
<h3>Details</h3>
<ul>
<li><strong>Date:</strong> Thursday, June 24 through Friday, June 25</li>
<li><strong>Start:</strong> 00:00 UTC June 24</li>
<li><strong>End:</strong> 23:59 UTC June 25</li>
<li><strong>Sign up:</strong> Participants do not have to sign up, just show up!</li>
<li><strong>Slack room:</strong> <a href="https://kubernetes.slack.com/archives/C02491CNJKZ">#sig-node-bug-scrub</a> on Kubernetes Slack</li>
<li><strong>Docs:</strong> See the <a href="https://hackmd.io/@sig-node-bug-scrub/S1gQnDbjO">HackMD docs</a>.</li>
<li><strong>Email:</strong> See the <a href="https://groups.google.com/g/kubernetes-dev/c/w2ghO4ihje0">kubernetes-dev mailing list</a>.</li>
</ul>
<h2>I'm an existing contributor, what should I work on?</h2>
<p>Work on triaging and closing SIG Node bugs. We have a lot of bugs!!</p>
<p>The goal of our event is to categorize, clean up, and resolve some of the <a href="https://github.com/kubernetes/kubernetes/issues?q=is%3Aopen+is%3Aissue+label%3Asig%2Fnode">450+
issues in k/k for SIG Node</a>.</p>
<p>Check out the <a href="https://hackmd.io/@sig-node-bug-scrub/S1gQnDbjO">event docs</a> for more instructions.</p>
<h2>I'm a new contributor and want to join but I have no idea what I'm doing!</h2>
<p>At some point, that was all of us!</p>
<p>This is a great opportunity to get involved if you've never contributed to
Kubernetes. We'll have dedicated mentors available to coordinate and help out
new contributors.</p>
<p>If you've never contributed to Kubernetes before, I recommend you check out the
<a href="https://github.com/kubernetes/community/blob/master/contributors/guide/README.md">Getting Started</a>
and <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/README.md">Contributor Guide</a>
resources in advance of the event. You will want to ensure you've signed the
<a href="https://github.com/kubernetes/community/blob/master/CLA.md">contributor license agreement</a> (CLA).</p>
<p>Remember, you <strong>don't have to code</strong> to make valuable contributions! Triaging
the bug tracker is a great example of this.</p>
<h2>See you there!</h2>
<p>Happy hacking.</p>Easy risotto2020-12-16T20:00:00-05:002020-12-16T20:00:00-05:00Elana Hashmantag:hashman.ca,2020-12-16:/risotto/<p>Risotto, 45 minutes from start to finish?! Yes, it <em>is</em> possible.</p><p>I have a secret: risotto doesn't <em>have</em> to involve standing over a pot,
stirring for 30 minutes. There is a better way.</p>
<p>I've made risotto using the "stir 5ever" method and frankly this one is half
the work and just as good. I took inspiration from <a href="https://www.seriouseats.com/recipes/2011/10/how-to-make-perfect-risotto-recipe.html">Kenji
Lopez-Alt</a>, who describes how you can make risotto with only a few
minutes of stirring at the end! Yes, this is a secret too good to keep to
myself.</p>
<p>The linked recipe is too rich for a weeknight dinner, at least for my tastes,
so I've included my typical modifications below, which omits the heavy cream. I
usually make mushroom risotto, because all the ingredients are long-lived
pantry ingredients, which means I can make this on a whim without having to
grocery shop!</p>
<blockquote class="twitter-tweet tw-align-center" data-conversation="none" data-lang="en" data-dnt="true">
<p lang="und" dir="ltr">👀 <a href="https://t.co/9gD4OGLZDs">pic.twitter.com/9gD4OGLZDs</a>
</p>— e. hashman (@ehashdn)
<a href="https://twitter.com/ehashdn/status/1339400747721478144?ref_src=twsrc%5Etfw">December 17, 2020</a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2>Mushroom risotto</h2>
<p>Serves 3 as a main course. Active time: 25m. Total time: 45m.</p>
<p>Ingredients:</p>
<ul>
<li>1oz (28g) dried mushrooms, such as porcini (or morels!)</li>
<li>3 ½ cups chicken or vegetable stock</li>
<li>1 ½ cups short-grained rice (I use arborio)</li>
<li>½ cup white wine</li>
<li>2 tbsp olive oil</li>
<li>2 tbsp unsalted butter</li>
<li>4 cloves garlic, minced</li>
<li>1 small onion, small diced</li>
<li>3oz (85g) grated Parmesan cheese</li>
<li>herbs for garnish (optional)</li>
</ul>
<p>Recipe:</p>
<ol>
<li>Leave mushrooms to rehydrate in 1 cup of hot water for at least 10 minutes.</li>
<li>Set a wide saucepan (about 3.5qts) or skillet on medium heat.</li>
<li>Mix stock and wine for a total of 4 cups. You can used homemade stock (the
best), boxed, or reconstitute from bouillon (my usual). If you use bouillon,
I find you get the best flavour if you use a mix, so I tend to use half
"no-chicken" and half "organic chicken" of the Better than Bouillon brand.
Let this cool if you used boiling water; you don't want it to be too hot.</li>
<li>Mix the rice and stock in a large bowl, agitating it to release the starch.
Reserve stock and set the rice in a strainer to drain.</li>
<li>Small dice an onion. Add olive oil and butter to the pan, and then add
the onion. Cook the onion until it's translucent, but don't brown it.</li>
<li>Increase the heat to high and add the rice to the pan to toast until it
becomes a little bit brown, stirring occasionally, about 3-4 minutes. Set a
timer so it doesn't burn 😀</li>
<li>Meanwhile, drain the mushrooms, making sure you reserve the broth. Chop
mushrooms and mince the garlic. Once the rice is toasted, add mushrooms and
garlic and stir.</li>
<li>Stir the reserved stock. Add all of the mushroom liquid and all but 1 cup of
the starchy stock to the pan. Bring to a boil.</li>
<li>Once it has reached a boil, cover, reduce to the lowest heat, and allow the
rice to simmer undisturbed for 10 minutes. In the meantime, you can grate
the cheese.</li>
<li>After 10 minutes, stir the rice once, shake the pan to ensure it is level,
and return to the heat for 10 more minutes. Sit back, relax, and bask in the
very little stirring you are doing.</li>
<li>At this point, the rice should be nearly cooked. Give it a stir, and add
the remaining cup of broth. Stir until fully incorporated, turn the heat up
to high, and cook until the risotto reaches your preferred consistency. I
like mine rather thick.</li>
<li>Once the rice is fully cooked, turn off the heat and stir in the grated
cheese a bit at a time, reserving some for garnish. Taste, and season with
salt and pepper.</li>
<li>Garnish with the grated cheese, and perhaps some herbs. Serve and enjoy!</li>
</ol>
<h3>Variations</h3>
<p><strong>No mushrooms:</strong> Okay, okay, you hate mushrooms! Sorry! You can leave them
out. Don't bother with the mushrooms, and add broth to make up for the lost
liquid.</p>
<p><strong>Mushroom <em>lover</em>:</strong> Alternatively, if you <em>love</em> mushrooms and you have fresh
ones available, use ⅓ to ½ a pound (150-225g) chopped fresh
mushrooms instead of dried. Add them at the same time you add the onions to
ensure all the liquid cooks off.</p>
<p><strong>Milanese, with stuff:</strong> One of my favourite variations on this dish is a
version with shrimp, asparagus, and saffron. Leave the mushrooms out, increase
the wine to ¾ or even 1 cup (seafood likes a little extra acidity),
ensure you have a total of 5 cups of broth, and add a few strands of saffron.
Chop asparagus and shrimp, season with salt and pepper, sauté them with some
butter or olive oil in a separate pan so they don't overcook, and fold them in
at the very end, after you've added the cheese.</p>
<p><strong>I MUST SUFFER:</strong> This was too easy? Want to stir more??? Fine, <a href="https://youtu.be/MSI4ZHVSatw">have it your
$*!#ing way.</a></p>
<p>I'm sure you can be creative and come up with something on your own...</p>Three talks at DebConf 20202020-09-05T00:00:00-04:002020-09-05T00:00:00-04:00Elana Hashmantag:hashman.ca,2020-09-05:/debconf-2020/<p>I spoke on three panels at DebConf 2020 ("DebConf at Home"). Here are the video recordings as well as brief summaries of each appearance.</p><p>This year has been a really unusual one for in-person events like conferences.
I had already planned to take this year off from travel for the most part,
attending just a handful of domestic conferences. But the pandemic has thrown
those plans into chaos; I do not plan to attend large-scale in-person events
until July 2021 at the earliest, per my employer's guidance.</p>
<p>I've been really sad to have turned down multiple speaking invitations this
year. To try to set expectations, I added a note to my <a href="/talks">Talks</a> page
that indicates I will not be writing any new talks for 2020, but am happy to
join panels or reprise old talks.</p>
<p>And somehow, with all that background, I still ended up giving <em>three</em> talks at
DebConf 2020 this year. In part, I think it's because this is the first DebConf
I've been able to attend since 2017, and I was so happy to have the
opportunity! I took time off work to give myself enough space to focus on the
conference. International travel is very difficult for me, so DebConf is
generally challenging if not impossible for me to attend.</p>
<h2>A panel a day keeps the FTP Team away?</h2>
<p>On Thursday, August 27th, I spoke on the <a href="https://debconf20.debconf.org/talks/46-leadership-in-debian-bofpanel/">Leadership in Debian</a>
panel, where I discussed some of the challenges leadership in the project must
face, including an appropriate response to the BLM movement and sustainability
for volunteer positions that require unsustainable hours (such as DPL).</p>
<div style="text-align: center">
<video width="600" height="333" controls>
<source src="https://meetings-archive.debian.net/pub/debian-meetings/2020/DebConf20/46-leadership-in-debian-bofpanel.webm"
type="video/webm">
Your browser does not support the video tag.
</video>
</div>
<p>On Friday, August 28th, I hosted the <a href="https://debconf20.debconf.org/talks/33-clojure-packaging-team-bof/">Debian Clojure BoF</a>, attended by
members of the Clojure and Puppet teams. The Puppet team is working to package
the latest versions of Puppet Server/DB, which involve significant Clojure
components, and I am doing my best to help.</p>
<div style="text-align: center">
<video width="600" height="333" controls>
<source src="https://meetings-archive.debian.net/pub/debian-meetings/2020/DebConf20/33-clojure-packaging-team-bof.webm"
type="video/webm">
Your browser does not support the video tag.
</video>
</div>
<p>On Saturday, August 29th, I spoke on the <a href="https://debconf20.debconf.org/talks/16-meet-the-technical-committee/">Meet the Technical Committee</a>
panel. The Committee presented a number of proposals for improving how we work
within the project. I was responsible for presenting our first proposal on
allowing folks to engage the committee privately.</p>
<div style="text-align: center">
<video width="600" height="333" controls>
<source src="https://meetings-archive.debian.net/pub/debian-meetings/2020/DebConf20/16-meet-the-technical-committee.webm"
type="video/webm">
Your browser does not support the video tag.
</video>
</div>My term at the Open Source Initiative thus far2020-09-02T12:15:00-04:002020-09-02T12:15:00-04:00Elana Hashmantag:hashman.ca,2020-09-02:/osi-2020/<p>Reflecting on 18 months serving as a Director for the Open Source Initiative.</p><p>When I ran for the OSI board in early 2019, I <a href="/osi">set three goals for
myself</a>:</p>
<ul>
<li>Grow the OSI's membership, and build a more representative organization.</li>
<li>Defend the Open Source Definition and FOSS commons.</li>
<li>Define the future of open source, as part of the larger community.</li>
</ul>
<p>Now that the OSI has announced <a href="https://opensource.org/node/1080">hiring an interim General Manager</a>, I
thought it would be a good time to publicly reflect on what I've accomplished
and what I'd like to see next.</p>
<p>As I promised in <a href="https://wiki.opensource.org/bin/Main/OSI%20Board%20of%20Directors/Board%20Member%20Elections/Hashman2019">my campaign pitch</a>, I aim to be publicly
accountable :)</p>
<h2>Growing the OSI's membership</h2>
<p>I have served as our Membership Committee Chair since the May 2019 board
meeting, tasked with devising and supervising strategy to increase membership
and deliver value to members.</p>
<p>As part of my election campaign last year, I signed up over 50 new individual
members. Since May 2019, we've seen strong 33% growth of individual members, to
reach a new all-time high over 600 (638 when I last checked).</p>
<p>I see the OSI as a relatively neutral organization that occupies a unique
position to build bridges among organizations within the FOSS ecosystem. In
order to facilitate this, we need a representative membership, and we need to
engage those members and provide forums for cross-pollination. As Membership
Committee Chair, I have been running quarterly video calls on Jitsi for our
affiliate members, where we can share updates between many global organizations
and discuss challenges we all face.</p>
<p>But it's not enough just to hold the discussion; we also need to bring fresh
new voices into the conversation. Since I've joined the board, I'm thrilled to
say that 16 new affiliate members joined (in chronological order) for a total
of 81:</p>
<ul>
<li><a href="https://www.brandeis.edu/">Brandeis University</a></li>
<li><a href="https://wiki.osmfoundation.org/wiki/Main_Page">OpenStreetMap Foundation</a></li>
<li><a href="https://todogroup.org/">TODO Group</a></li>
<li><a href="https://osf.dev/">OpenStack Foundation</a></li>
<li><a href="https://www.nwtime.org/">Network Time Foundation</a></li>
<li><a href="https://www.sourcefabric.org/">Sourcefabric</a></li>
<li><a href="https://ocf.tw/">Open Culture Foundation</a></li>
<li><a href="https://fuss.bz.it/">FUSS Project</a></li>
<li><a href="https://fossasia.org/">FOSSASIA</a></li>
<li><a href="https://openpreservation.org/">Open Preservation Foundation</a></li>
<li><a href="https://www.oscafrica.org/">Open Source Community Africa</a></li>
<li><a href="http://www.openforumeurope.org/">Open Forum Europe</a></li>
<li><a href="https://www.gnome.org/foundation/">GNOME Foundation</a></li>
<li><a href="https://openjsf.org/">OpenJS Foundation</a></li>
<li><a href="https://www.oasis-open.org/">OASIS</a></li>
<li><a href="https://openuk.uk/">OpenUK</a></li>
</ul>
<p>I was also excited to run a survey of the OSI's individual and affiliate
membership to help inform the future of the organization that received 58
long-form responses. The survey has been accepted by the board at our August
meeting and should be released publicly soon!</p>
<h2>Defending the Open Source Definition</h2>
<p>When I joined the board, the first committee I joined was the License
Committee, which is responsible for running the <a href="https://opensource.org/approval">licence review process</a>,
making recommendations on new licenses, and maintaining our existing licenses.</p>
<p>Over the past year, under <a href="https://opensource.org/docs/board-annotated#PamelaChestek">Pamela Chestek</a>'s leadership as Chair, the full
board has approved the following licenses (with SPDX identifiers in brackets)
on the recommendation of the License Committee:</p>
<ul>
<li><a href="http://lists.opensource.org/pipermail/license-review_lists.opensource.org/2019-June/004247.html">LBNL BSD License</a> (BSD-3-Clause-LBNL)</li>
<li><a href="http://lists.opensource.org/pipermail/license-review_lists.opensource.org/2019-September/004416.html">OpenLDAP Public License Version 2.8</a> (OLDAP-2.8)</li>
<li><a href="https://lists.opensource.org/pipermail/license-review_lists.opensource.org/2020-February/004694.html">CAL Beta 4</a> (CAL-1.0)</li>
<li><a href="http://lists.opensource.org/pipermail/license-review_lists.opensource.org/2020-February/004695.html">Mulan PSL v2</a> (MulanPSL-2.0)</li>
<li><a href="http://lists.opensource.org/pipermail/license-review_lists.opensource.org/2020-April/004807.html">BSD 1 Clause</a> (BSD-1-Clause)</li>
<li><a href="http://lists.opensource.org/pipermail/license-review_lists.opensource.org/2020-May/004841.html">PHP License 3.01</a> (PHP-3.01)</li>
<li><a href="http://lists.opensource.org/pipermail/license-review_lists.opensource.org/2020-June/004890.html">The Unlicense</a> (Unlicense)</li>
<li><a href="http://lists.opensource.org/pipermail/license-review_lists.opensource.org/2020-August/004915.html">MIT No Attribution</a> (MIT-0)</li>
</ul>
<p>We withheld approval of the following licenses:</p>
<ul>
<li><a href="http://lists.opensource.org/pipermail/license-review_lists.opensource.org/2019-August/004280.html">CAL v1.0 (initial draft)</a></li>
<li><a href="http://lists.opensource.org/pipermail/license-review_lists.opensource.org/2020-January/004645.html">Vaccine License</a></li>
</ul>
<p>I've also worked to define the scope of work for hiring someone to <a href="https://wiki.opensource.org/bin/LicenseReviewToolRequirements/">improve
our license review</a> process, which we have an <a href="https://opensource.org/node/1078">open RFP</a> for!</p>
<h2>Chopping wood and carrying water</h2>
<p>I joined the OSI with the goal of improving an organization I didn't think was
performing up to its potential. Its membership and board were not
representative of the wider open source community, its messaging felt outdated,
and it seemed to be failing to rise to today's challenges for FOSS.</p>
<p>But before one can rise to meet these challenges, you need a strong foundation.
The OSI needed the organizational structure, health, and governance in order to
address such questions. Completing that work is essential, but not exactly
glamourous—and it's a place that I thrive. Honestly, I don't (yet?) want
to be the public face of the organization, and I apologize to those who've
missed me at events like FOSDEM.</p>
<p>I want to talk a little about some of my behind-the-scenes activities that I've
completed as part of my board service:</p>
<ul>
<li>Pushing for organizational <a href="https://en.wikipedia.org/wiki/Directors_and_officers_liability_insurance">D&O and liability insurance</a>, which we
obtained in July 2020</li>
<li>Recruiting and encouraging new board members: I helped recruit both our
appointed board members, <a href="https://opensource.org/docs/board-annotated#DebBryant">Deb Bryant</a> and <a href="https://opensource.org/docs/board-annotated#TracyHinds">Tracy Hinds</a>,
who now serve as officers, and <a href="https://twitter.com/ehashdn/status/1233471774660136962">endorsed</a>
<a href="https://opensource.org/docs/board-annotated#MeganByrd-Sanicki">Megan Byrd-Sanicki</a>'s candidacy, also a board officer</li>
<li>Recruiting and interviewing a new <a href="https://opensource.org/node/1080">interim General Manager</a>, Deb
Nicholson</li>
<li>Forming an Incubator Project Working Group to determine graduation criteria
for OSI Incubator Projects and serving as <a href="https://snowdrift.coop/">Snowdrift.coop</a>'s board
sponsor</li>
</ul>
<p>All of this work is intended to improve the organization's health and provide
it with an excellent foundation for its mission.</p>
<h2>Defining the future of open source</h2>
<p>Soon after I was elected to the board, I gave a talk at Brooklyn.js entitled
<a href="https://github.com/brooklynjs/brooklynjs.github.io/issues/557">"The Future of Open Source."</a> In this presentation, I pondered about the
history and future of the free and open source software movement, and the
ethical questions we must face.</p>
<p>In my election campaign, I wrote "Software licenses are a means, not an end, to
open source software. Focusing on licensing is necessary but not sufficient to
ensure a vibrant, thriving open source community. Focus on licensing to the
exclusion of other serious community concerns is to our collective detriment."</p>
<p>My primary goal for my first term on the board was to ensure the OSI would be
positioned to answer wider questions about the open source community and its
future beyond licenses. Over the past two months, I supported Megan
Byrd-Sanicki's suggestion to hold (and then participated in, with the rest of
the board) organizational strategy sessions to facilitate our long-term
planning. My contribution to help inform these sessions was providing the
member survey on behalf of the Membership Committee.</p>
<p>Now, I think we are much better equiped to face the hard questions we'll have
to tackle. In my opinion, the Open Source Initiative is better positioned than
ever to answer them, and I can't wait to see what the future brings.</p>
<p>Hope to see you at our first <a href="https://opensource.org/StateOfTheSource">State of the Source</a> conference next week!</p>Presenter mode in LibreOffice Impress without an external display2020-05-27T21:15:00-04:002020-05-27T21:15:00-04:00Elana Hashmantag:hashman.ca,2020-05-27:/libreoffice/<p>LibreOffice impress, for some reason, does not support presenter mode with a single display. So I found a workaround.</p><p>I typically use LibreOffice Impress for my talks, much to some folks' surprise.
Yes, you <em>can</em> make slides look okay with free software! But there's one
annoying caveat that has bothered me for ages.</p>
<p>Impress makes it nearly impossible to enter presenter mode with a single
display, while also displaying slides. I have never understood this limitation,
but it's existed for a minimum of <a href="https://bugs.documentfoundation.org/show_bug.cgi?id=62179"><em>seven years</em></a>.</p>
<p>I've tried all sorts of workarounds over the years, including <a href="https://bz.apache.org/ooo/show_bug.cgi?id=121873#c12">a macro that
forces LibreOffice into presenter mode</a>, which I never was able to
figure out how to reverse once I ran it...</p>
<p>This has previously been an annoyance but never posed a big problem, because
when push came to shove I could leave my house and use an external monitor or
screen when presenting at meetups. But now, everything's virtual, I'm in
lockdown indefinitely, and I don't have another display available at home. And
about 8 hours before speaking at a meetup today, I realized I didn't have a way
to share my slides while seeing my speaker notes. Uh oh.</p>
<p>So I got this stupid idea.</p>
<p>...why don't I just placate LibreOffice with a FAKE display?</p>
<h2>Virtual displays with <code>xrandr</code></h2>
<p>My GPU had this capability innately, it turns out, if I could just whisper the
right incantations to unlock its secrets:</p>
<div class="highlight"><pre><span></span><code>ehashman@red-dot:~$<span class="w"> </span>cat<span class="w"> </span>/usr/share/X11/xorg.conf.d/20-intel.conf<span class="w"> </span>
Section<span class="w"> </span><span class="s2">"Device"</span>
<span class="w"> </span>Identifier<span class="w"> </span><span class="s2">"intelgpu0"</span>
<span class="w"> </span>Driver<span class="w"> </span><span class="s2">"intel"</span>
<span class="w"> </span>Option<span class="w"> </span><span class="s2">"VirtualHeads"</span><span class="w"> </span><span class="s2">"1"</span>
EndSection
</code></pre></div>
<p>After restarting X to allow this newly created config to take effect, I now
could see two new virtual displays available for use:</p>
<div class="highlight"><pre><span></span><code>ehashman@red-dot:~$<span class="w"> </span>xrandr
Screen<span class="w"> </span><span class="m">0</span>:<span class="w"> </span>minimum<span class="w"> </span><span class="m">8</span><span class="w"> </span>x<span class="w"> </span><span class="m">8</span>,<span class="w"> </span>current<span class="w"> </span><span class="m">3840</span><span class="w"> </span>x<span class="w"> </span><span class="m">1080</span>,<span class="w"> </span>maximum<span class="w"> </span><span class="m">32767</span><span class="w"> </span>x<span class="w"> </span><span class="m">32767</span>
eDP1<span class="w"> </span>connected<span class="w"> </span>primary<span class="w"> </span>1920x1080+0+0<span class="w"> </span><span class="o">(</span>normal<span class="w"> </span>left<span class="w"> </span>inverted<span class="w"> </span>right<span class="w"> </span>x<span class="w"> </span>axis<span class="w"> </span>y<span class="w"> </span>axis<span class="o">)</span><span class="w"> </span>310mm<span class="w"> </span>x<span class="w"> </span>170mm
<span class="w"> </span>1920x1080<span class="w"> </span><span class="m">60</span>.01*+<span class="w"> </span><span class="m">59</span>.93<span class="w"> </span>
<span class="w"> </span>...
<span class="w"> </span>640x360<span class="w"> </span><span class="m">59</span>.84<span class="w"> </span><span class="m">59</span>.32<span class="w"> </span><span class="m">60</span>.00<span class="w"> </span>
DP1<span class="w"> </span>disconnected<span class="w"> </span><span class="o">(</span>normal<span class="w"> </span>left<span class="w"> </span>inverted<span class="w"> </span>right<span class="w"> </span>x<span class="w"> </span>axis<span class="w"> </span>y<span class="w"> </span>axis<span class="o">)</span>
DP2<span class="w"> </span>disconnected<span class="w"> </span><span class="o">(</span>normal<span class="w"> </span>left<span class="w"> </span>inverted<span class="w"> </span>right<span class="w"> </span>x<span class="w"> </span>axis<span class="w"> </span>y<span class="w"> </span>axis<span class="o">)</span>
HDMI1<span class="w"> </span>disconnected<span class="w"> </span><span class="o">(</span>normal<span class="w"> </span>left<span class="w"> </span>inverted<span class="w"> </span>right<span class="w"> </span>x<span class="w"> </span>axis<span class="w"> </span>y<span class="w"> </span>axis<span class="o">)</span>
HDMI2<span class="w"> </span>disconnected<span class="w"> </span><span class="o">(</span>normal<span class="w"> </span>left<span class="w"> </span>inverted<span class="w"> </span>right<span class="w"> </span>x<span class="w"> </span>axis<span class="w"> </span>y<span class="w"> </span>axis<span class="o">)</span>
VIRTUAL1<span class="w"> </span>disconnected<span class="w"> </span><span class="o">(</span>normal<span class="w"> </span>left<span class="w"> </span>inverted<span class="w"> </span>right<span class="w"> </span>x<span class="w"> </span>axis<span class="w"> </span>y<span class="w"> </span>axis<span class="o">)</span>
VIRTUAL2<span class="w"> </span>disconnected<span class="w"> </span><span class="o">(</span>normal<span class="w"> </span>left<span class="w"> </span>inverted<span class="w"> </span>right<span class="w"> </span>x<span class="w"> </span>axis<span class="w"> </span>y<span class="w"> </span>axis<span class="o">)</span>
</code></pre></div>
<p>Nice. Now, to actually use it:</p>
<div class="highlight"><pre><span></span><code>ehashman@red-dot:~$<span class="w"> </span>xrandr<span class="w"> </span>--addmode<span class="w"> </span>VIRTUAL1<span class="w"> </span>1920x1080
ehashman@red-dot:~$<span class="w"> </span>xrandr<span class="w"> </span>--output<span class="w"> </span>VIRTUAL1<span class="w"> </span>--mode<span class="w"> </span>1920x1080<span class="w"> </span>--right-of<span class="w"> </span>eDP1
</code></pre></div>
<p>And indeed, after running these commands, I found myself with a virtual
display, very happy to black hole all my windows, available to the imaginary
right of my laptop screen.</p>
<p>This allowed me to mash that "Present" button in LibreOffice and get my
presenter notes on my laptop display, while putting my actual slides in a
virtual time-out that I could still screenshare!</p>
<p>Wouldn't it be nice if LibreOffice just fixed the actual bug? 🤷</p>
<h2>Well, actually...</h2>
<p>I must forgive myself for my stage panic. The talk ended up going great, and
the immediate problem was solved. But it turns out this bug <em>has</em> been
addressed upstream! It's just... not well-documented.</p>
<p>A couple years ago, there was <a href="https://ask.libreoffice.org/en/question/166523/is-it-possible-to-enter-the-presenter-console-in-impress-without-external-monitor/">a forum post on ask.libreoffice.org</a> that
featured this exact question, and a solution was provided!</p>
<blockquote>
<p>Yes, use Open Expert Configuration via <code>Tools > Options > LibreOffice >
Advanced.</code> Search for <code>StartAlways</code>. You should get a node
<code>org.openoffice.Office.PresenterScreen</code> with line <code>Presenter</code>. Double-click that
line to toggle the boolean value to <code>true</code>.</p>
</blockquote>
<p>I tested this out locally and... yeah that works. But it turns out the bug from
2013 had not been updated with this solution <a href="https://bugs.documentfoundation.org/show_bug.cgi?id=62179#c12">until just a few months
ago</a>.</p>
<p>There are very limited search results for this configuration property. I wish
this was much better documented. But so it goes with free software; here's a
hack and a real solution as we all try to improve things :)</p>Beef and broccoli2020-05-25T19:30:00-04:002020-05-25T19:30:00-04:00Elana Hashmantag:hashman.ca,2020-05-25:/beef-broccoli/<p>This is my recipe for Chinese-influenced beef and broccoli.</p><p>Beef and broccoli is one of my favourite easy weeknight meals. It's savoury,
it's satisfying, it's mercifully quick, and so, so delicious.</p>
<p>The recipe here is based on <a href="https://www.simplyrecipes.com/recipes/broccoli_beef/">this
one</a> but with a few
simplifications and modifications. The ingredients are basically the same, but
the technique is a little different. Oh, and I include some fermented black
beans because they're yummy :3</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true"><p lang="en" dir="ltr">
Made 🥩 and 🥦 <a
href="https://t.co/cSpyA3hzP6">pic.twitter.com/cSpyA3hzP6</a></p>— e.
hashman (@ehashdn) <a
href="https://twitter.com/ehashdn/status/1252404252795797504?ref_src=twsrc%5Etfw">April
21, 2020</a></blockquote>
<script async
src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2>🥩 and 🥦</h2>
<p>Serves 3. Active time: 20-30m. Total time: 30m.</p>
<p>Ingredients:</p>
<ul>
<li>¾ lb steak, thinly sliced (leave it in the freezer for ~30m to make
slicing easier)</li>
<li>¾ lb broccoli florets</li>
<li>3 large cloves garlic, chopped</li>
<li>1 teaspoon fermented black beans, rinsed and chopped (optional)</li>
</ul>
<p>For the marinade:</p>
<ul>
<li>1 teaspoon soy sauce</li>
<li>1 teaspoon shaoxing rice wine or dry sherry</li>
<li>½ teaspoon corn starch</li>
<li>⅛ teaspoon ground black pepper</li>
</ul>
<p>For the sauce:</p>
<ul>
<li>2 tablespoons oyster sauce</li>
<li>2 teaspoons soy sauce</li>
<li>1 teaspoon shaoxing rice wine or dry sherry</li>
<li>¼ cup chicken broth (I use <a href="https://www.betterthanbouillon.com/">Better than
Bouillon</a>)</li>
<li>2 tablespoons water</li>
<li>1 teaspoon corn starch</li>
</ul>
<p>For the rice:</p>
<ul>
<li>¾ cup white medium-grain rice (I use calrose)</li>
<li>1¼ cup water</li>
<li>large pinch of salt</li>
</ul>
<p>Recipe:</p>
<ol>
<li>Begin by preparing the rice: combine rice, water, and salt in a small
saucepan and bring to a boil. Once it reaches a boil, cover and reduce to a
simmer for 20 minutes. Then turn off the heat and let it rest.</li>
<li>Mix the marinade in a bowl large enough to hold the meat.</li>
<li>Thinly slice the beef. Place slices in the marinade bowl and toss to evenly
coat.</li>
<li>Start heating a wok or similar pan on medium-high to high heat. This will
ensure you get a good sear.</li>
<li>Prep the garlic, black beans, and broccoli. If you use frozen broccoli, you
can save some time here :)</li>
<li>Combine the ingredients for the sauce and mix well to ensure the corn starch
doesn't form any lumps.</li>
<li>By this point the beef should have been marinating for about 10-15m. Add a
tablespoon of neutral cooking oil (like canola or soy) to the meat, give it
one more toss to ensure it's thoroughly coated, then add a layer of meat to
the dry pan. You may need to sear in batches. On high heat, cooking will
take 1-2 minutes per side, ensuring each is nicely browned. Once the strips
are cooked, remove from the pan and set aside.</li>
<li>While the beef is cooking, steam the broccoli until it's almost (but not
quite) cooked through. I typically do this by putting it in a large bowl,
adding 2 tbsp water, covering it and microwaving: 3 minutes on high if
frozen, 1½ minutes on high if fresh, pausing halfway through to stir.</li>
<li>Once all the beef is cooked and set aside, spray the pan or add oil to coat
and add the garlic and black bean (if using). Stir and cook for 30-60
seconds until fragrant.</li>
<li>When the garlic is ready, give the sauce a quick stir and add it to the pan,
using it to deglaze. Once there are no more bits stuck to the bottom and the
sauce is thick to your liking, add the broccoli and beef to the pan. Mix
until thoroughly coated in sauce and heated through. Taste and adjust
seasoning (salt, pepper, soy, etc.) if necessary.</li>
<li>Fluff the rice and serve. Enjoy!</li>
</ol>
<h3>But I am a vegetarian???</h3>
<p>Seitan can substitute for the beef relatively easily. Finding a substitute for
oyster sauce is a little bit trickier, especially since it's the star
ingredient. You can buy vegetarian or vegan oyster sauce (they're usually
mushroom-based), but I have no idea how good they taste. You can also try
making it at home! If you do, let me know how it turns out?</p>Repack Zoom .debs to remove the `ibus` dependency2020-04-09T00:00:00-04:002020-04-09T00:00:00-04:00Elana Hashmantag:hashman.ca,2020-04-09:/zoom/<p>Zoom .deb files have a frustrating dependency on the ibus package. Let me show you how to remove it.</p><p>For whatever reason, <a href="https://zoom.us/">Zoom</a> distributes .debs that have a
dependency on <code>ibus</code>. <code>ibus</code> is the "intelligent input bus" package and as far
as I'm aware, might be used for emoji input in chat or something?? But is
otherwise not actually a dependency of the Zoom package. I've tested this
extensively... the client works fine without it.</p>
<p>I noticed when I installed <code>ibus</code> along with the Zoom package that <code>ibus</code> would
frequently eat an entire core of CPU. I'm sure this is a bug in the <code>ibus</code>
package or service, but I have no energy to try to get that fixed. If it's not
a hard dependency, Zoom <em>shouldn't depend on it in the first place.</em></p>
<p>Anyways, here's how you can repack a Zoom .deb to remove the <code>ibus</code> dependency:</p>
<div class="highlight"><pre><span></span><code><span class="nv">scratch</span><span class="o">=</span><span class="k">$(</span>mktemp<span class="w"> </span>-d<span class="k">)</span>
<span class="c1"># Extract package contents</span>
dpkg<span class="w"> </span>-x<span class="w"> </span>zoom_amd64.deb<span class="w"> </span><span class="nv">$scratch</span>
<span class="c1"># Extract package control information</span>
dpkg<span class="w"> </span>-e<span class="w"> </span>zoom_amd64.deb<span class="w"> </span><span class="nv">$scratch</span>/DEBIAN
<span class="c1"># Remove the ibus dependency</span>
sed<span class="w"> </span>-i<span class="w"> </span>-E<span class="w"> </span><span class="s1">'s/(ibus, |, ibus)//'</span><span class="w"> </span><span class="nv">$scratch</span>/DEBIAN/control
<span class="c1"># Rebuild the .deb</span>
dpkg<span class="w"> </span>-b<span class="w"> </span><span class="nv">$scratch</span><span class="w"> </span>patched_zoom_amd64.deb
</code></pre></div>
<p>Now you can install the patched .deb with</p>
<div class="highlight"><pre><span></span><code>dpkg<span class="w"> </span>-i<span class="w"> </span>patched_zoom_amd64.deb
</code></pre></div>
<p>The upstream fix would be for Zoom to move the <code>ibus</code> "Dependency" to a
"Recommends", but they have been unwilling to do this for over a year.</p>
<h3>But wait, what version even is my package?</h3>
<p>By the way, you may have <em>also</em> noticed that the Zoom client downloads do not
conform to the standard Debian package naming scheme (i.e. including the
version in the filename). If you're not sure what version a <code>zoom_amd64.deb</code>
package you've downloaded is, you can quickly extract that information with
<code>dpkg-deb</code>:</p>
<div class="highlight"><pre><span></span><code>dpkg-deb<span class="w"> </span>-I<span class="w"> </span>zoom_amd64.deb<span class="w"> </span><span class="p">|</span><span class="w"> </span>grep<span class="w"> </span>Version
<span class="c1"># Version: 3.5.383291.0407</span>
</code></pre></div>Chili2020-03-02T19:00:00-05:002020-03-02T19:00:00-05:00Elana Hashmantag:hashman.ca,2020-03-02:/chili/<p>This is my recipe for homemade chili.</p><p>I really like beans. This is one way for me to power through the copious
amounts I receive through the beloved <a href="https://www.ranchogordo.com/collections/samplers-and-sets/products/the-rancho-gordo-bean-club">Bean of the Month Club</a>. (It took
me many months, but I finally got in!)</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true"><p lang="en" dir="ltr">
Cooked beans two days in a row in order to deliver this masterpiece <a
href="https://t.co/VzIlPKKosJ">pic.twitter.com/VzIlPKKosJ</a></p>
— e. hashman (@ehashdn) <a href="https://twitter.com/ehashdn/status/1234669958426005505?ref_src=twsrc%5Etfw">March 3, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2>Beef Chili</h2>
<p>Serves 5-7. Active time: 20m. Total time: 90m.</p>
<p>Ingredients:</p>
<ul>
<li>1lb lean ground beef</li>
<li>1 large onion</li>
<li>1 poblano pepper (or use your favourite peppers)</li>
<li>1 stick celery</li>
<li>some fresh garlic (idk, 2-3 cloves?)</li>
<li>2 tablespoons ancho chili powder</li>
<li>3½ teaspoons ground cumin</li>
<li>2 teaspoons oregano</li>
<li>2 teaspoons garlic powder</li>
<li>14oz can diced tomatoes</li>
<li>14oz can crushed tomatoes</li>
<li>3 cups big cooked beans (I love <a href="https://www.ranchogordo.com/products/ayocote-morado">ayacote morado</a> for this)</li>
<li>3 cups small cooked beans (pinto work great)</li>
<li>salt to taste</li>
<li>wedge of lime (optional)</li>
<li>garnishes (sour cream, grated cheese, green onion, etc.)</li>
</ul>
<p>Recipe:</p>
<ol>
<li>Heat a dutch oven on high.</li>
<li>Small or medium dice (depending on your preference) the celery, pepper, and
onion. You should have about four cups total.</li>
<li>Brown the beef in the dutch oven, using a little bit of cooking oil to avoid
sticking if desired.</li>
<li>While the meat is browning, mince the garlic, measure the spices, and
combine these all together.</li>
<li>Once the meat is brown, add the diced vegetables and cook until tender and
the onion is translucent.</li>
<li>Add the spices and garlic, thoroughly combine, and cook until fragrant,
about one minute.</li>
<li>Add the tomatoes. Once the mixture comes to a boil, cover and simmer for
about an hour, stirring every ~20 minutes or so.</li>
<li>Add the beans and use bean broth or water to adjust consistency.</li>
<li>This is a good time to taste and add salt. I usually use 1 to 1½
teaspoons.</li>
<li>Bring to a boil again, cover and simmer for 10-15 more minutes.</li>
<li>Turn off the heat, taste, and adjust seasoning if necessary. Add a squeeze
of lime if it needs a little acidity.</li>
<li>Serve with sour cream, grated cheese, sliced green onions, or any of your
other favourite garnishes. </li>
<li>Enjoy!</li>
</ol>
<blockquote class="twitter-tweet tw-align-center" data-conversation="none" data-lang="en" data-dnt="true"><p lang="en" dir="ltr">
before and after <a href="https://t.co/CgkeCDPOaO">pic.twitter.com/CgkeCDPOaO</a></p>— e. hashman (@ehashdn) <a href="https://twitter.com/ehashdn/status/1234671272870629377?ref_src=twsrc%5Etfw">March 3, 2020</a></blockquote>
<h3>Frequently Asked Questions</h3>
<p><strong>Q:</strong> <em>Why wait until basically the end to add salt?</em>
<br>
<strong>A:</strong> Because canned ingredients will contain salt, and it's hard to predict
how much, so you should always salt to taste. Waiting until the end will always
ensure you season accurately.</p>
<p><strong>Q:</strong> <em>Why are so many of these directions so hand-wavey?</em>
<br>
<strong>A:</strong> Cooking is an adventure. Experiment a little!</p>
<p><strong>Q:</strong> <em>But I have no idea what "to taste" means.</em>
<br>
<strong>A:</strong> Taste it, add the thing, stir, taste it again. Is it salty enough? Sour
enough? Sweet enough? You don't know, you say? Keep going. Add a little too
much and then you'll find out for next time. 😁</p>
<p><strong>Q:</strong> <em>Why do you add so many beans and so little meat?</em>
<br>
<strong>A:</strong> I really like beans.</p>KubeCon NA 2019 Talk Resources2020-01-04T00:00:00-05:002020-01-04T00:00:00-05:00Elana Hashmantag:hashman.ca,2020-01-04:/kubecon-2019/<p>Talk resources for "Weighing a Cloud: Measuring Your Kubernetes Clusters", copresented at KubeCon NA 2019 with Han Kang.</p><p>At <a href="https://kccncna19.sched.com/event/UaeP">KubeCon + CloudNativeCon North America 2019</a>, I co-presented
"Weighing a Cloud: Measuring Your Kubernetes Clusters" with <a href="https://github.com/logicalhan">Han Kang</a>.
Here's some links and resources related to my talk, for your reference.</p>
<h2>Weighing a Cloud: Measuring Your Kubernetes Clusters</h2>
<ul>
<li><a href="https://static.sched.com/hosted_files/kccncna19/95/Weighing%20a%20Cloud%3A%20Measuring%20Your%20Kubernetes%20Clusters.pdf">Talk slides (pdf download)</a></li>
<li><a href="https://www.youtube.com/watch?v=BknenxQ_NLQ">Talk video, hosted on YouTube</a></li>
<li><a href="/srecon-2019">Related talk from SREcon:</a> Kubernetes SLOs and debugging
cluster issues</li>
<li><a href="https://github.com/ehashman/k8s-monitoring-demo">Try it yourself: sample code on GitHub</a></li>
<li><a href="https://prometheus.io/docs/introduction/overview">Prometheus documentation</a></li>
</ul>
<iframe style="display: block; margin: auto;" width="560" height="315" src="https://www.youtube-nocookie.com/embed/BknenxQ_NLQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<h3>Related readings</h3>
<p>I'm including these documents for reference to add some context around what's
currently happening (as of 2019Q4) in the Kubernetes instrumentation SIG and
wider ecosystem.</p>
<p>Note that GitHub links are pinned to their most recent commit to ensure they will not break; if you want the latest version, make sure to switch the branch to "master".</p>
<ul>
<li><a href="https://docs.google.com/document/d/17emKiwJeqfrCsv0NZ2FtyDbenXGtTNCsDEiLbPa7x7Y/edit">SIG Instrumentation Meeting Minutes</a> (note: you must join the <a href="https://groups.google.com/forum/#!forum/kubernetes-sig-instrumentation">Google
Group</a> to be able to access these)</li>
<li><a href="https://github.com/kubernetes/enhancements/blob/f6af521711a28222726d8e5027c54ceceb02a417/keps/sig-instrumentation/20181106-kubernetes-metrics-overhaul.md">Kubernetes 1.14 metrics overhaul</a></li>
<li><a href="https://github.com/kubernetes/enhancements/blob/f6af521711a28222726d8e5027c54ceceb02a417/keps/sig-instrumentation/20190404-kubernetes-control-plane-metrics-stability.md">Metrics Stability Framework</a></li>
<li><a href="https://github.com/kubernetes/enhancements/blob/f6af521711a28222726d8e5027c54ceceb02a417/keps/sig-instrumentation/20190605-metrics-stability-migration.md">Metrics Stability Migration Plan</a></li>
<li><a href="https://github.com/kubernetes/community/blob/3547a212cc56c04657763b96759e51109c5c79a0/contributors/design-proposals/instrumentation/monitoring_architecture.md">Kubernetes monitoring architecture</a></li>
<li><a href="https://github.com/kubernetes/community/blob/3547a212cc56c04657763b96759e51109c5c79a0/contributors/devel/sig-instrumentation/instrumentation.md">Kubernetes instrumentation guidelines</a></li>
</ul>My favourite bash alias for git2019-08-03T00:00:00-04:002019-08-03T00:00:00-04:00Elana Hashmantag:hashman.ca,2019-08-03:/git-bash-alias/<p>Here's a non-trivial bash alias that I use in my day-to-day git workflow you might find handy.</p><p>I review a lot of code. <a href="https://github.com/ehashman#user-activity-overview"><em>A lot.</em></a> And an important part of that
process is getting to experiment with said code so I can make sure it actually
works. As such, I find myself with a frequent need to locally run code from a
submitted patch.</p>
<p>So how does one fetch that code? Long ago, when I was a new maintainer, I would
add the remote repository I was reviewing to my local repo so I could fetch
that whole fork and target branch. Once downloaded, I could play around with
that on my local machine. But this was a lot of overhead! There was a lot of
clicking, copying, and pasting involved in order to figure out the clone URL
for the remote repo, and a bunch of commands to set it up. It felt like a lot
of toil that could be easily automated, but I didn't know a better way.</p>
<p>One day, when <a href="https://langui.sh/">a coworker of mine</a> saw me struggling with this, he showed
me the better way.</p>
<p>Turns out, most hosted git repos with pull request functionality will let you
pull down a read-only version of the changeset from the upstream fork using
git, meaning that you <em>don't</em> have to set up additional remote tracking to
fetch and run the patch or use platform-specific HTTP APIs.</p>
<h2>Using GitHub's git references for pull requests</h2>
<p>I first learned how to do this on GitHub.</p>
<p>GitHub maintains a copy of pull requests against a particular repo at the
<a href="https://help.github.com/en/articles/checking-out-pull-requests-locally#modifying-an-inactive-pull-request-locally"><code>pull/NUM/head</code> reference</a>. (More documentation on refs <a href="https://git-scm.com/book/en/v2/Git-Internals-Git-References">here</a>.)
This means that if you have set up a remote called <code>origin</code> and someone submits
a pull request #123 against that repository, you can fetch the code by running</p>
<div class="highlight"><pre><span></span><code>$ git fetch origin pull/123/head
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 4 (delta 3), reused 3 (delta 3), pack-reused 1
Unpacking objects: 100% (4/4), done.
From github.com:ehashman/hack_the_planet
* branch refs/pull/123/head -> FETCH_HEAD
$ git checkout FETCH_HEAD
Note: checking out 'FETCH_HEAD'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at deadb00 hack the planet!!!
</code></pre></div>
<p>Woah.</p>
<h3>Using pull request references for CI</h3>
<p><strong>As a quick aside:</strong> This is also handy if you want to write your own CI
scripts against users' pull requests. Even better—on GitHub, you can
fetch a tree with the pull request <em>already merged</em> onto the top of the current
master branch by fetching <code>pull/NUM/merge</code>. (I'm not sure if this is officially
documented somewhere, and I don't believe it's widely supported by other hosted
git platforms.)</p>
<p>If you also specify the <code>--depth</code> flag in your fetch command, you can fetch
code even faster by limiting how much upstream history you download. It doesn't
make much difference on small repos, but it is a big deal on large projects:</p>
<div class="highlight"><pre><span></span><code><span class="n">elana</span><span class="nv">@silverpine</span><span class="err">:</span><span class="o">/</span><span class="n">tmp</span><span class="err">$</span><span class="w"> </span><span class="nc">time</span><span class="w"> </span><span class="n">git</span><span class="w"> </span><span class="n">clone</span><span class="w"> </span><span class="nl">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="p">.</span><span class="n">com</span><span class="o">/</span><span class="n">kubernetes</span><span class="o">/</span><span class="n">kubernetes</span><span class="p">.</span><span class="n">git</span>
<span class="n">Cloning</span><span class="w"> </span><span class="k">into</span><span class="w"> </span><span class="s1">'kubernetes'</span><span class="p">...</span>
<span class="nl">remote</span><span class="p">:</span><span class="w"> </span><span class="n">Enumerating</span><span class="w"> </span><span class="nl">objects</span><span class="p">:</span><span class="w"> </span><span class="mi">295</span><span class="p">,</span><span class="w"> </span><span class="n">done</span><span class="p">.</span>
<span class="nl">remote</span><span class="p">:</span><span class="w"> </span><span class="n">Counting</span><span class="w"> </span><span class="nl">objects</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="o">%</span><span class="w"> </span><span class="p">(</span><span class="mi">295</span><span class="o">/</span><span class="mi">295</span><span class="p">),</span><span class="w"> </span><span class="n">done</span><span class="p">.</span>
<span class="nl">remote</span><span class="p">:</span><span class="w"> </span><span class="n">Compressing</span><span class="w"> </span><span class="nl">objects</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="o">%</span><span class="w"> </span><span class="p">(</span><span class="mi">167</span><span class="o">/</span><span class="mi">167</span><span class="p">),</span><span class="w"> </span><span class="n">done</span><span class="p">.</span>
<span class="nl">remote</span><span class="p">:</span><span class="w"> </span><span class="n">Total</span><span class="w"> </span><span class="mi">980446</span><span class="w"> </span><span class="p">(</span><span class="n">delta</span><span class="w"> </span><span class="mi">148</span><span class="p">),</span><span class="w"> </span><span class="n">reused</span><span class="w"> </span><span class="mi">136</span><span class="w"> </span><span class="p">(</span><span class="n">delta</span><span class="w"> </span><span class="mi">128</span><span class="p">),</span><span class="w"> </span><span class="n">pack</span><span class="o">-</span><span class="n">reused</span><span class="w"> </span><span class="mi">980151</span>
<span class="n">Receiving</span><span class="w"> </span><span class="nl">objects</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="o">%</span><span class="w"> </span><span class="p">(</span><span class="mi">980446</span><span class="o">/</span><span class="mi">980446</span><span class="p">),</span><span class="w"> </span><span class="mf">648.95</span><span class="w"> </span><span class="n">MiB</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mf">12.47</span><span class="w"> </span><span class="n">MiB</span><span class="o">/</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">done</span><span class="p">.</span>
<span class="n">Resolving</span><span class="w"> </span><span class="nl">deltas</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="o">%</span><span class="w"> </span><span class="p">(</span><span class="mi">686795</span><span class="o">/</span><span class="mi">686795</span><span class="p">),</span><span class="w"> </span><span class="n">done</span><span class="p">.</span>
<span class="n">Checking</span><span class="w"> </span><span class="k">out</span><span class="w"> </span><span class="nl">files</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="o">%</span><span class="w"> </span><span class="p">(</span><span class="mi">20279</span><span class="o">/</span><span class="mi">20279</span><span class="p">),</span><span class="w"> </span><span class="n">done</span><span class="p">.</span>
<span class="nc">real</span><span class="w"> </span><span class="mi">1</span><span class="n">m31</span><span class="mf">.035</span><span class="n">s</span>
<span class="k">user</span><span class="w"> </span><span class="mi">1</span><span class="n">m17</span><span class="mf">.856</span><span class="n">s</span>
<span class="n">sys</span><span class="w"> </span><span class="mi">0</span><span class="n">m7</span><span class="mf">.782</span><span class="n">s</span>
<span class="n">elana</span><span class="nv">@silverpine</span><span class="err">:</span><span class="o">/</span><span class="n">tmp</span><span class="err">$</span><span class="w"> </span><span class="nc">time</span><span class="w"> </span><span class="n">git</span><span class="w"> </span><span class="n">clone</span><span class="w"> </span><span class="o">--</span><span class="k">depth</span><span class="o">=</span><span class="mi">10</span><span class="w"> </span><span class="nl">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="p">.</span><span class="n">com</span><span class="o">/</span><span class="n">kubernetes</span><span class="o">/</span><span class="n">kubernetes</span><span class="p">.</span><span class="n">git</span><span class="w"> </span><span class="n">kubernetes</span><span class="o">-</span><span class="n">shallow</span>
<span class="n">Cloning</span><span class="w"> </span><span class="k">into</span><span class="w"> </span><span class="s1">'kubernetes-shallow'</span><span class="p">...</span>
<span class="nl">remote</span><span class="p">:</span><span class="w"> </span><span class="n">Enumerating</span><span class="w"> </span><span class="nl">objects</span><span class="p">:</span><span class="w"> </span><span class="mi">34305</span><span class="p">,</span><span class="w"> </span><span class="n">done</span><span class="p">.</span>
<span class="nl">remote</span><span class="p">:</span><span class="w"> </span><span class="n">Counting</span><span class="w"> </span><span class="nl">objects</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="o">%</span><span class="w"> </span><span class="p">(</span><span class="mi">34305</span><span class="o">/</span><span class="mi">34305</span><span class="p">),</span><span class="w"> </span><span class="n">done</span><span class="p">.</span>
<span class="nl">remote</span><span class="p">:</span><span class="w"> </span><span class="n">Compressing</span><span class="w"> </span><span class="nl">objects</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="o">%</span><span class="w"> </span><span class="p">(</span><span class="mi">22976</span><span class="o">/</span><span class="mi">22976</span><span class="p">),</span><span class="w"> </span><span class="n">done</span><span class="p">.</span>
<span class="nl">remote</span><span class="p">:</span><span class="w"> </span><span class="n">Total</span><span class="w"> </span><span class="mi">34305</span><span class="w"> </span><span class="p">(</span><span class="n">delta</span><span class="w"> </span><span class="mi">17247</span><span class="p">),</span><span class="w"> </span><span class="n">reused</span><span class="w"> </span><span class="mi">19060</span><span class="w"> </span><span class="p">(</span><span class="n">delta</span><span class="w"> </span><span class="mi">10567</span><span class="p">),</span><span class="w"> </span><span class="n">pack</span><span class="o">-</span><span class="n">reused</span><span class="w"> </span><span class="mi">0</span>
<span class="n">Receiving</span><span class="w"> </span><span class="nl">objects</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="o">%</span><span class="w"> </span><span class="p">(</span><span class="mi">34305</span><span class="o">/</span><span class="mi">34305</span><span class="p">),</span><span class="w"> </span><span class="mf">34.22</span><span class="w"> </span><span class="n">MiB</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mf">10.25</span><span class="w"> </span><span class="n">MiB</span><span class="o">/</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">done</span><span class="p">.</span>
<span class="n">Resolving</span><span class="w"> </span><span class="nl">deltas</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="o">%</span><span class="w"> </span><span class="p">(</span><span class="mi">17247</span><span class="o">/</span><span class="mi">17247</span><span class="p">),</span><span class="w"> </span><span class="n">done</span><span class="p">.</span>
<span class="nc">real</span><span class="w"> </span><span class="mi">0</span><span class="n">m31</span><span class="mf">.495</span><span class="n">s</span>
<span class="k">user</span><span class="w"> </span><span class="mi">0</span><span class="n">m3</span><span class="mf">.941</span><span class="n">s</span>
<span class="n">sys</span><span class="w"> </span><span class="mi">0</span><span class="n">m1</span><span class="mf">.228</span><span class="n">s</span>
</code></pre></div>
<h2>Writing the <code>pull</code> alias</h2>
<p>So how can one harness all this as a bash alias? It takes just a little bit of
code:</p>
<div class="highlight"><pre><span></span><code>pull<span class="o">()</span><span class="w"> </span><span class="o">{</span>
<span class="w"> </span>git<span class="w"> </span>fetch<span class="w"> </span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span><span class="w"> </span>pull/<span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>/head<span class="w"> </span><span class="o">&&</span><span class="w"> </span>git<span class="w"> </span>checkout<span class="w"> </span>FETCH_HEAD
<span class="o">}</span>
<span class="nb">alias</span><span class="w"> </span><span class="nv">pull</span><span class="o">=</span><span class="s1">'pull'</span>
</code></pre></div>
<p>Then I can check out a PR locally with the short command <code>pull <remote> <num></code>:</p>
<div class="highlight"><pre><span></span><code>$ pull origin 123
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Total 5 (delta 4), reused 4 (delta 4), pack-reused 1
Unpacking objects: 100% (5/5), done.
From github.com:ehashman/hack_the_planet
* branch refs/pull/123/head -> FETCH_HEAD
Note: checking out 'FETCH_HEAD'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at deadb00 hack the planet!!!
</code></pre></div>
<p>You can even add your own commits, save them on a local branch, and push that
to your collaborator's repository to build on their PR if you're so inclined...
but let's not get too ahead of ourselves.</p>
<h2>Changeset references on other git platforms</h2>
<p>These pull request refs are not a special feature of git itself, but rather a
per-platform implementation detail using an arbitrary git ref format. As far as
I'm aware, most major git hosting platforms implement this, but they all use
slightly different ref names.</p>
<h3>GitLab</h3>
<p>At my last job I needed to figure out how to make this work with GitLab in
order to set up CI pipelines with our Jenkins instance. Debian's <a href="https://salsa.debian.org/">Salsa</a>
platform also runs GitLab.</p>
<p>GitLab calls user-submitted changesets "merge requests" and that language is
reflected here:</p>
<div class="highlight"><pre><span></span><code>git fetch origin merge-requests/NUM/head
</code></pre></div>
<p>They also have some <a href="https://gitlab.com/help/user/project/merge_requests/index.md#checkout-merge-requests-locally">nifty documentation</a> for adding a git alias to
fetch these references. They do so in a way that creates a local branch
automatically, if that's something you'd like—personally, I check out so
many patches that I would not be able to deal with cleaning up all the extra
branch mess!</p>
<h3>BitBucket</h3>
<p>Bad news: as of the time of publication, <a href="https://bitbucket.org/site/master/issues/5814/reify-pull-requests-by-making-them-a-ref">this isn't supported on
bitbucket.org</a>, even though a request for this feature has been open
for seven years. (BitBucket Server supports this feature, but that's standalone
and proprietary, so I won't bother including it in this post.)</p>
<h3>Gitea</h3>
<p>While I can't find any official documentation for it, I tested and confirmed
that Gitea uses the same ref names for pull requests as GitHub, and thus you
can use the same bash/git aliases on a Gitea repo as those you set up for
GitHub.</p>
<h2>Saved you a click?</h2>
<p>Hope you found this guide handy. No more excuses: now that it's just one short
command away, go forth and run your colleagues' code locally!</p>How to grant (Tom Marble) Debian Maintainer access2019-07-24T00:00:00-04:002019-07-24T00:00:00-04:00Elana Hashmantag:hashman.ca,2019-07-24:/dm-access/<p>I needed to give Tom Marble DM access to some team packages, but I keep forgetting how I did that previously, so I'll document it in this post.</p><p>I run the Debian Clojure Team, which means that occasionally folks volunteer to
help out with Clojure packaging. This is awesome! Since I'm lazy, I don't want
to have to sponsor every package upload for folks who have proven their
aptitude at packaging. Hence, sometimes I need to grant Debian Maintainers
upload access to team packages.</p>
<p>Folks typically point at <a href="https://lists.debian.org/debian-devel-announce/2012/09/msg00008.html">this email</a> as documentation of how to grant
DM access on packages. However, I have zero desire to hand-craft artisanal
<code>dak</code> commands. So, I try to leverage some existing tools I already have
installed on my system to help me out—namely, the <a href="https://manpages.debian.org/buster/dput-ng/dcut.1.en.html"><code>dcut</code></a> tool
from the <a href="https://packages.debian.org/buster/dput-ng"><code>dput-ng</code> package</a>.</p>
<h2>The commands</h2>
<p>Tom Marble wanted DM access to the <a href="https://packages.debian.org/buster/libjava-jdbc-clojure"><code>libjava-jdbc-clojure</code> package</a>,
after I suggested he try doing a new version upload for it. I previously gave
him DM access to maintain <code>shimdandy</code> and <code>com-hypirion-io-clojure</code>. But I
couldn't remember exactly how I did it...</p>
<p>According to the <code>dcut</code> manpage, this should be as simple as running</p>
<div class="highlight"><pre><span></span><code>dcut<span class="w"> </span>dm<span class="w"> </span>--uid<span class="w"> </span><span class="s2">"Tom Marble"</span><span class="w"> </span>--allow<span class="w"> </span>libjava-jdbc-clojure
</code></pre></div>
<p>However, there is a slight problem: I don't normally run <code>dput</code> (or <code>dcut</code>) on
a machine with my Debian key present, since I keep my only copy on my laptop.
For various reasons (mostly related to intertia, external monitors, and wifi
drivers), I run Linux Mint on my laptop, and the version of <code>dcut</code> available
there doesn't actually work properly, so I can't just run <code>dcut</code> locally...</p>
<p>What to do about this?</p>
<p>It turns out that there is an <em>undocumented</em> flag, <code>-S</code> or <code>--save</code>, that will
save the generated commands locally.</p>
<div class="highlight"><pre><span></span><code>dcut<span class="w"> </span>-s<span class="w"> </span>-S<span class="w"> </span>dm<span class="w"> </span>--uid<span class="w"> </span><span class="s2">"Tom Marble"</span><span class="w"> </span>--allow<span class="w"> </span>libjava-jdbc-clojure
</code></pre></div>
<p>The <code>-s</code> flag, or <code>--simulate</code>, ensures that we don't try to upload the file to
the archive just yet. This will produce a file in the current directory with a
name similar to <code>ehashman-1564016122.dak-commands</code>. Take a look:</p>
<div class="highlight"><pre><span></span><code>ehashman@corn-syrup:~$<span class="w"> </span>cat<span class="w"> </span>ehashman-1564016122.dak-commands
Archive:<span class="w"> </span>ftp.upload.debian.org
Uploader:<span class="w"> </span>Elana<span class="w"> </span>Hashman<span class="w"> </span><ehashman@debian.org>
Action:<span class="w"> </span>dm
Fingerprint:<span class="w"> </span>884A52C4AC8ABB931D158FA840BFEE868B055D9A
Allow:<span class="w"> </span>libjava-jdbc-clojure
</code></pre></div>
<p>Now is a good time to verify that the key and package is correct. You can then
sign this file:</p>
<div class="highlight"><pre><span></span><code>gpg<span class="w"> </span>--sign<span class="w"> </span>--armour<span class="w"> </span>--clearsign<span class="w"> </span>ehashman-1564016122.dak-commands
</code></pre></div>
<p>And use <code>dcut</code> to upload it:</p>
<div class="highlight"><pre><span></span><code>dcut<span class="w"> </span>upload<span class="w"> </span>-f<span class="w"> </span>ehashman-1564016122.dak-commands
</code></pre></div>
<p>Once the file has been processed, check the <a href="https://ftp-master.debian.org/dm.txt">FTP Master DM log</a> to make
sure your DM changes have been set correctly.</p>
<p>See you on the next episode of "me creating problems for myself with scary
Debian tools" 👋</p>
<h2>References</h2>
<ul>
<li><code>dput-ng</code> <a href="http://dput-ng.debian.net">documentation</a></li>
<li><code>dcut</code> <a href="https://manpages.debian.org/buster/dput-ng/dcut.1.en.html">manpage</a></li>
<li><a href="https://ftp-master.debian.org/dm.txt">Current DM permissions</a></li>
</ul>PyCon 2019 Talk Resources2019-05-01T00:00:00-04:002019-05-01T00:00:00-04:00Elana Hashmantag:hashman.ca,2019-05-01:/pycon-2019/<p>Talk resources for "The Black Magic of Python Wheels", presented at PyCon US 2019.</p><p>At PyCon US in 2019, I reprised my talk "The Black Magic of Python Wheels",
originally presented at <a href="/pygotham-2018">PyGotham 2018</a>. I based this talk on
my three years of work on <a href="https://github.com/pypa/auditwheel/"><code>auditwheel</code></a> and the <a href="https://github.com/pypa/manylinux"><code>manylinux</code>
platform</a>, hoping to share some dark details of how the proverbial
sausage is made.</p>
<p>After this talk, I will be <a href="/leaving-pypa">retiring from <code>auditwheel</code>
maintainership</a>.</p>
<h2>The Black Magic of Python Wheels</h2>
<ul>
<li><a href="https://us.pycon.org/2019/schedule/presentation/186/">Talk page, PyCon website</a></li>
<li><a href="https://www.youtube.com/watch?v=02aAZ8u3wEQ">Talk video, hosted on YouTube</a></li>
<li><a href="/pycon-2019/ehashman-pycon2019-slides.pdf">Talk slides (pdf download)</a></li>
<li><a href="https://pythonwheels.com"><code>pythonwheels.com</code></a></li>
<li><a href="https://github.com/pypa/python-manylinux-demo">Simple <code>manylinux</code> wheelbuilding demo</a></li>
</ul>
<iframe style="display: block; margin: auto;" width="560" height="315" src="https://www.youtube-nocookie.com/embed/02aAZ8u3wEQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<h3>Follow-up readings</h3>
<ul>
<li><a href="http://man7.org/linux/man-pages/man5/elf.5.html">Linux Programmer's Manual: format of Executable and Linkable Format (ELF) Files</a></li>
<li><a href="http://www.man7.org/tlpi/">The Linux Programming Interface</a>: 42.3.2 "Symbol Versioning", pg. 870. <em>(Note: all of
chapter 42 would be a great resource on the topic! This is a great book.)</em></li>
<li><a href="https://www.akkadia.org/drepper/symbol-versioning">ELF Symbol Versioning</a></li>
<li><a href="http://web.archive.org/web/20160107032111/http://www.trevorpounds.com/blog/?p=103">Linking to Older Versioned Symbols</a></li>
<li><a href="https://www.technovelty.org/linux/plt-and-got-the-key-to-code-sharing-and-dynamic-libraries.html">PLT and GOT - the key to code sharing and dynamic libraries</a></li>
</ul>
<h4>All the PEPs referenced in the talk</h4>
<p>In increasing numeric order.</p>
<ul>
<li><a href="https://www.python.org/dev/peps/pep-0376/">PEP 376</a> "Database of Installed Python Distributions"</li>
<li><a href="https://www.python.org/dev/peps/pep-0426/">PEP 426</a> "Metadata for Python Software Packages 2.0"</li>
<li><a href="https://www.python.org/dev/peps/pep-0427/">PEP 427</a> "The Wheel Binary Package Format 1.0"</li>
<li><a href="https://www.python.org/dev/peps/pep-0513/">PEP 513</a> "A Platform Tag for Portable Linux Built Distributions" (aka <code>manylinux1</code>)</li>
<li><a href="https://www.python.org/dev/peps/pep-0571/">PEP 571</a> "The <code>manylinux2010</code> Platform Tag"</li>
</ul>
<h3>Image licensing info</h3>
<ul>
<li><a href="https://plixs.com/photo/3297/tree-cat-silhouette-sunset">Tree Cat Silhouette Sunset</a>: Public Domain (CC0) @besshamiti</li>
<li><a href="https://flic.kr/p/ArW1N9">Happy Halloween! (Costume Dog)</a>: Public Domain (CC0) @milkyfactory</li>
</ul>I'm stepping down as maintainer of auditwheel2019-04-10T22:15:00-04:002019-04-10T22:15:00-04:00Elana Hashmantag:hashman.ca,2019-04-10:/leaving-pypa/<p>After three years of work on auditwheel and the manylinux toolchain, I will be stepping away to focus on other open source projects.</p><p>For the <a href="https://github.com/pypa/auditwheel/graphs/contributors?from=2016-04-01&type=c&to=2019-04-11">last three years</a> I've been a regular contributor and
core maintainer of <a href="https://github.com/pypa/auditwheel"><code>auditwheel</code></a>, a Python Packaging Authority (or
"PyPA") tool used to build portable binary/extension wheels on Linux.
<code>auditwheel</code>'s "show" command allows developers to check if their Python
wheel's external symbol dependencies comply with the <a href="https://www.python.org/dev/peps/pep-0513">manylinux</a>
<a href="https://www.python.org/dev/peps/pep-0571/">policies</a>, and its "repair" command enables developers to more
easily build policy-compliant wheels inside an appropriate environment like a
<a href="https://github.com/pypa/manylinux">manylinux Docker image</a> without having to make significant changes
to their build processes.</p>
<p>Most recently, at the last Python Packaging Authority sprints in November 2018,
I finished work to <a href="https://github.com/pypa/auditwheel/pull/92">support the <code>manylinux2010</code> platform tag in
<code>auditwheel</code></a>. After extensive testing, this functionality was
<a href="https://github.com/pypa/auditwheel/releases">released</a> in <a href="https://github.com/pypa/auditwheel/releases/tag/2.0">version 2.0</a> in January of 2019.</p>
<h2>But why?</h2>
<p><code>auditwheel</code> is a very technically challenging tool to maintain. It requires
deep knowledge of dynamic linking, ELF binaries, and symbol versioning on the
Linux platform. While this is very exciting technical work, it's not the sort
of project that I can work on sustainably in my free time and off hours. I'm
currently the only active <code>auditwheel</code> maintainer, and I don't feel like I can
give the project the attention it deserves on an ongoing basis, especially
given community interest in <a href="https://discuss.python.org/t/the-next-manylinux-specification/1043">updating the manylinux
specification</a> and <a href="https://github.com/pypa/auditwheel/issues/144">supporting new platform
policies</a>.</p>
<p>On the bright side, concluding my work with <code>auditwheel</code> and manylinux will
allow me to dedicate more quality time to other FOSS projects I'm excited
about! In a personal capacity, I have just started a two year term as an
individual member of the <a href="https://opensource.org/board">Open Source Initiative Board of Directors</a>, and
I will continue my <a href="https://qa.debian.org/developer.php?login=ehashman&comaint=yes">work in Debian</a>. In a professional capacity, I
recently started a new job at Red Hat and I intend to significantly increase my
upstream Kubernetes and OpenShift contributions over the next year.</p>
<p>I'm making this announcement now to avoid surprising anyone at PyCon, and I'd
love to spend my time at the conference working on a transition plan. I will be
giving <a href="https://us.pycon.org/2019/schedule/presentation/186/">an introductory talk about <code>auditwheel</code></a> and the manylinux
toolchain if you're interested in learning more about the space and want to get
involved! At PyCon, I hope I will have the opportunity to provide some outgoing
input on the future of <code>auditwheel</code> and manylinux, especially after <a href="https://github.com/pypa/manylinux/issues/179">the bumpy
rollout of <code>manylinux2010</code></a>.</p>
<p>So long and thanks for all the fish 🐟</p>SREcon19 Americas Talk Resources2019-03-22T00:00:00-04:002019-03-22T00:00:00-04:00Elana Hashmantag:hashman.ca,2019-03-22:/srecon-2019/<p>Talk resources for "Operating within Normal Parameters: Monitoring Kubernetes", presented at SREcon19 Americas.</p><p>At <a href="https://www.usenix.org/conference/srecon19americas/presentation/hashman">SREcon19 Americas</a>, I gave a talk called "Operating within Normal
Parameters: Monitoring Kubernetes". I also reprised this talk at the <a href="https://www.meetup.com/Cloud-Native-PDX/events/265784684/">Cloud
Native PDX meetup</a> in October 2019 and the <a href="https://www.meetup.com/Portland-DevOps-GroundUp/events/djhzznybchbkc/">Portland DevOps
meetup</a> in May 2020. Here's some links and resources related to my
talk, for your reference.</p>
<h2>Operating within Normal Parameters: Monitoring Kubernetes</h2>
<ul>
<li><a href="/srecon-2019/srecon-2019-ehashman.pdf">Talk slides (pdf download)</a></li>
<li><a href="https://www.youtube.com/watch?v=GDo-Ixy_i9k">Talk video, hosted on YouTube</a></li>
<li><a href="https://github.com/ehashman/srecon-2019">Try it yourself: sample code on GitHub</a></li>
<li><a href="https://prometheus.io/docs/introduction/overview">Prometheus documentation</a></li>
<li><a href="https://github.com/kubernetes/kube-state-metrics"><code>kube-state-metrics</code> documentation</a></li>
<li><a href="https://github.com/kubernetes-incubator/metrics-server"><code>metrics-server</code> documentation</a></li>
<li><a href="http://docs.grafana.org/">Grafana documentation</a> (for dashboards and visualization)</li>
</ul>
<iframe style="display: block; margin: auto;" width="560" height="315" src="https://www.youtube-nocookie.com/embed/GDo-Ixy_i9k" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<h3>Additional Prometheus metrics sources</h3>
<ul>
<li><a href="https://github.com/prometheus/node_exporter"><code>node_exporter</code></a></li>
<li><a href="https://github.com/prometheus/blackbox_exporter"><code>blackbox_exporter</code></a></li>
</ul>
<h3>Related readings</h3>
<p>I'm including these documents for reference to add some context around what's
currently happening (as of 2019Q1) in the Kubernetes instrumentation SIG and
wider ecosystem.</p>
<p>Note that GitHub links are pinned to their most recent commit to ensure they will not break; if you want the latest version, make sure to switch the branch to "master".</p>
<ul>
<li><a href="https://docs.google.com/document/d/17emKiwJeqfrCsv0NZ2FtyDbenXGtTNCsDEiLbPa7x7Y/edit">SIG Instrumentation Meeting Minutes</a> (note: you must join the <a href="https://groups.google.com/forum/#!forum/kubernetes-sig-instrumentation">Google
Group</a> to be able to access these)</li>
<li><a href="https://github.com/kubernetes/enhancements/blob/aaa166020d6fbfc5a32354588c9170c6085dddfb/keps/sig-instrumentation/0031-kubernetes-metrics-overhaul.md">Kubernetes 1.14 metrics overhaul (KEP-0031)</a></li>
<li><a href="https://github.com/kubernetes/community/blob/4fb3de39b0afa844098e48a1c1f2b6fdd064e91e/contributors/design-proposals/instrumentation/core-metrics-pipeline.md">Core Metrics proposal</a></li>
<li><a href="https://github.com/kubernetes/enhancements/blob/aaa166020d6fbfc5a32354588c9170c6085dddfb/keps/sig-node/kubelet-resource-metrics-endpoint.md">Kubelet Resource Metrics (formerly "Core Metrics") Endpoint proposal</a></li>
<li><a href="https://github.com/kubernetes/community/blob/4fb3de39b0afa844098e48a1c1f2b6fdd064e91e/contributors/design-proposals/instrumentation/monitoring_architecture.md">Kubernetes monitoring architecture</a></li>
<li><a href="https://github.com/kubernetes/community/blob/4fb3de39b0afa844098e48a1c1f2b6fdd064e91e/contributors/devel/sig-instrumentation/instrumentation.md">Kubernetes instrumentation guidelines</a></li>
</ul>I'm running for the Open Source Initiative Board of Directors2019-03-02T11:00:00-05:002019-03-02T11:00:00-05:00Elana Hashmantag:hashman.ca,2019-03-02:/osi/<p>I'm running as an individual member for the Board of Directors of the Open Source Initiative. I hope you'll consider joining and lending me your vote!</p><p>The <a href="https://opensource.org/elections">2019 election</a> for the Open Source Initiative Board of Directors
is upon us, and I'm running for a seat on the board as an Individual Member.</p>
<p>The <a href="https://opensource.org/">Open Source Initiative (OSI)</a> is a <del>shadowy cabal</del> global
non-profit organization that is primarily responsible for maintaining the <a href="https://opensource.org/osd-annotated">Open
Source Definition</a> and <a href="https://opensource.org/licenses/alphabetical">list of approved Open Source Licenses</a>,
in addition to promoting and representing the wider open source community. If
you use or care about open source software, the OSI impacts you!</p>
<h2>Why am I running?</h2>
<p>There are three main things that I'd like to accomplish as a board member:</p>
<ul>
<li>Grow the OSI's membership, and build a more representative organization.</li>
<li>Defend the Open Source Definition and FOSS commons.</li>
<li>Define the future of open source, as part of the larger community.</li>
</ul>
<p>You can read more of the specifics of <a href="https://wiki.opensource.org/bin/Main/OSI+Board+of+Directors/Board+Member+Elections/Hashman2019">my platform</a> on the OSI
election wiki.</p>
<h2>Why vote for me?</h2>
<p><strong>My platform is actionable, specific, and measurable.</strong> For example, when I
say I want to grow the OSI's membership, this isn't just a platitude: I'm
running a membership drive to recruit 25 new members and I've nearly met my
goal already! If you vote for me, it's easy for you to hold me accountable to
these specific commitments.</p>
<p><strong>I have a cross-cutting, boots-on-the-ground view of the challenges and needs
of the FOSS community.</strong> I'm active as a developer in many different open
source communities: the Python community, the Clojure community, the JavaScript
community, the Kubernetes and CNCF communities. I bring a new, broad
perspective to the Board as a primarily technical contributor from communities
who are not yet deeply involved with the OSI, and pledge to represent their
interests.</p>
<p><strong>As a packager, I have a practical understanding of software licensing and
redistribution.</strong> Defending the Open Source Definition isn't just about
ideology for me: threats to the FOSS commons directly impact my work as a
downstream developer. Packaging FOSS for distribution in Debian, applying
patches, and sharing derivative works all require a deep understanding of the
practice of software licenses and copyright. Hence, I have important and
applicable experience for <a href="https://opensource.org/approval">license review</a>.</p>
<h2>This sounds great, sign me up!</h2>
<p>If you like what you've seen here, I'd be thrilled if you considered <a href="https://opensource.org/members/join">joining
the OSI as a new member</a> and voting for me.</p>
<p><a href="https://opensource.org/members/join"><strong>You can sign up for a membership here.</strong></a></p>
<p>Do let me know if I was your inspiration for joining, as I'd love to count you
towards my membership drive! You can reach out via <a href="https://toot.cat/@ehashman">Mastodon</a>,
<a href="https://twitter.com/ehashdn">Twitter</a>, or <a href="mailto:osi-2019-election@hashman.ca">email</a>. I'll also
make sure to send you a reminder to vote in the election. Voting opens Monday,
March 4 at 12:00am PST and closes Friday, March 15 at 11:59PM PST.</p>get that (multigrain) bread 🍞2019-02-23T17:15:00-05:002019-02-23T17:15:00-05:00Elana Hashmantag:hashman.ca,2019-02-23:/multigrain-bread/<p>Some ramblings about my adventures in breadmaking, and a recipe for my most successful multigrain loaf.</p><p>I've been baking a lot of bread over the past year: about one loaf a week, give
or take. It's a fun hobby, and it saves me a bit of money because homemade
bread lasts longer than the stuff at the grocery store and also costs less.
There are some occupational hazards, like burning my arm, but the payoff seems
to be mostly worth it, because homemade bread tastes <em>so good</em>. The thing that
got me hooked was this <a href="https://www.seriouseats.com/recipes/2010/05/bread-baking-the-simplest-white-bread-recipe-ever.html">basic recipe</a> from Donna
Currie—something about the overnight rest and use of good olive oil make
this loaf really magical, and the active time is minimal which made it easy for
me to get into baking regularly!</p>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true">
<p lang="en" dir="ltr">My beautiful golden son :')
<a href="https://t.co/dNnwd0j8ZH">pic.twitter.com/dNnwd0j8ZH</a>
</p>— (:e hashman) (@ehashdn)
<a href="https://twitter.com/ehashdn/status/1081640710909165568?ref_src=twsrc%5Etfw">January 5, 2019</a>
</blockquote>
<p>A thing I really appreciate about baking bread is that it's very forgiving.
I've only once ever made a loaf so bad that it turned out inedible, and that
was one among dozens. My bread-shaping technique was non-existent when I
started making bread, but now it's a lot better. I like making loaves on the
smaller side, but my loaf pans are the wrong size, so sometimes I end up with a
short, stout loaf—who cares. Every week I get the opportunity to try a
new experiment and see what works and what doesn't, and my technique and
knowledge get better every time. Your loaves will not judge you. You do not
have to be perfect.</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true">
<p lang="en" dir="ltr">Compare this guy to the first loaf I baked when I got into
bread earlier this year :D
<br><br>As they say, practice makes perfect.
<a href="https://t.co/nbF8l1e5UH">pic.twitter.com/nbF8l1e5UH</a>
</p>— (:e hashman) (@ehashdn)
<a href="https://twitter.com/ehashdn/status/1081651351724191744?ref_src=twsrc%5Etfw">January 5, 2019</a>
</blockquote>
<p>Since I am Serious about breadmaking—or, at least, relatively committed
to the cause—I decided to make my own sourdough starter in December of
last year. His name is <strong>Yeästeroth, Keeper of the Seal, Sourer of the Dough:
Yeästeroth the Undying, Who has Slept a Thousand Years... and Rises Again</strong>.
(Name courtesy of my roommate. Other names submitted for consideration include
"Allison the Mermaid" and "libsour.so.5".)</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true">
<p lang="en" dir="ltr">7 days of Yeästeroth! To celebrate, my roommate started playing a chant from Mozart's Requiem.
<br><br>
Still moving along slower than I would like so I have swaddled him in a blanket.
<a href="https://t.co/CceLXjz1Xy">pic.twitter.com/CceLXjz1Xy</a>
</p>— (:e hashman) (@ehashdn)
<a href="https://twitter.com/ehashdn/status/1071051104945872897?ref_src=twsrc%5Etfw">December 7, 2018</a>
</blockquote>
<p>According to various experiments, Yeästeroth sort of sucks at making dough rise
and I am not nearly patient enough to try to fix this. So I just appreciate his
nice flavour and spare him a little help in the rising department. When I bake
sourdough bread, I throw in an extra teaspoon of instant yeast and call it a day.</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true">
<p lang="en" dir="ltr">So it turns out that little bit of commercial yeast I was adding *was* doing something 😂 I have learned my lesson this week...
<br><br>
Still tastes great, dense but good texture!
<a href="https://t.co/O52jf8R9I0">pic.twitter.com/O52jf8R9I0</a>
</p>— (:e hashman) (@ehashdn)
<a href="https://twitter.com/ehashdn/status/1084218708988448769?ref_src=twsrc%5Etfw">January 12, 2019</a>
</blockquote>
<h2>Whole wheat: the final frontier</h2>
<p>While getting started with white bread and sourdough was relatively easy,
baking with whole wheat flour is... a challenge, to say the least. It's much
less forgiving than baking with white flour, the doughs need much more
hydration so they're sticky and make a mess, and they rise at a different rate
than white bread.</p>
<p>My favourite whole wheat breads tend to be multigrain, so that was the first
kind of whole wheat loaf I tried to bake. I wasn't super happy with it,
although I did bake <a href="https://www.allrecipes.com/recipe/17780/hearty-multigrain-bread/">the recipe</a> again to try to improve on my
technique, and that one turned out a bit better.</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true">
<p lang="en" dir="ltr">CRUMB
<a href="https://t.co/8LZNO7q2M5">pic.twitter.com/8LZNO7q2M5</a>
</p>— (:e hashman) (@ehashdn)
<a href="https://twitter.com/ehashdn/status/1023755927990755328?ref_src=twsrc%5Etfw">July 30, 2018</a>
</blockquote>
<p>To be honest I didn't think that getting better at baking whole wheat bread was
actually worth the effort, so I stopped doing it for a while. Whole wheat bread
is not morally superior to white bread, and it's way harder to work with and
make taste good, so why bother with all the trouble?</p>
<p>Well, one of my coworkers went to the UK over the winter break and brought
me back a bag of flour from his mother's local mill, and it turned out to be a
whole wheat seed mix flour for multigrain bread, so I felt <em>compelled</em> to get
better at this. Look at how charming it is! The label uses Comic Sans!!</p>
<div style="text-align: center">
<img alt="Wessex Mill Six Seed Bread Flour (a photo of the flour packaging)"
src="/multigrain-bread/sixseed.jpg" />
</div>
<p>Since this flour has seeds already mixed in, a new and exciting thing for me, I
looked for a bread recipe <a href="https://whatbread.wordpress.com/2013/02/24/wessex-mill-six-seed-bread-flour/">using it specifically</a> (which was
really just <a href="https://www.theguardian.com/lifeandstyle/wordofmouth/2010/jun/10/how-to-bake-wholemeal-bread">this recipe</a>, modified) and made some modifications
of my own (sourdough starter/instant yeast mix, no vitamin C tablet, half the
salt, less sugar and used olive oil in place of butter). That turned out pretty
well.</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true">
<p lang="en" dir="ltr">Baked my best multigrain loaf yet today! With the flour
my coworker gave me 😊 <a href="https://t.co/G4EYApU8zR">pic.twitter.com/G4EYApU8zR</a>
</p>— (:e hashman) (@ehashdn)
<a href="https://twitter.com/ehashdn/status/1091186059667484673?ref_src=twsrc%5Etfw">February 1, 2019</a>
</blockquote>
<p>While I baked a pretty decent first loaf with the Wessex Mill flour, it still
felt like it lacked something. I missed the butter and the brown sugar and so
this time I figured I'd be a little more decadent and go all out. But I didn't
really trust myself to do that with a 100% whole wheat loaf and succeed, so I
decided to try an experiment by using a little more white flour. This new
recipe is a hybridization of all the various multigrain loaf recipes I've tried
thus far, and it worked out better than I could have hoped for!</p>
<h2>My most successful multigrain loaf yet</h2>
<p>Ingredients:</p>
<ul>
<li>8oz <a href="https://www.wessexmill.co.uk/acatalog/Six-Seed-Bread-Flour-1.5kg-X053S.html">Wessex Mill Six Seed Flour</a></li>
<li>4oz white bread flour (I use King Arthur brand)</li>
<li>1 tsp instant yeast (I use Saf-instant red)</li>
<li>1 tsp salt</li>
<li>2 tbsp dark brown sugar</li>
<li>8oz sourdough starter at 100% hydration, unfed</li>
<li>6oz warm water</li>
<li>1-2 tbsp melted butter (it's fine if it's salted)</li>
<li>canola/vegetable oil for greasing</li>
</ul>
<p><strong>Note:</strong> This recipe involves a rest in the fridge. I recommend mixing the
dough in the evening and baking it in the next morning.</p>
<p>Recipe:</p>
<ol>
<li>Mix the dry ingredients in a stand mixer until thoroughly combined.</li>
<li>Measure in the wet ingredients and mix in the stand mixer until they come
together. At this point the dough will be an extremely sticky mess,
completely attached to the sides of the bowl.</li>
<li>Fear not. Add about a tablespoon of white bread flour and mix until it just
starts to unstick from the sides of the mixer bowl.</li>
<li>Grease a plastic bag with a bit of canola oil. Make sure you grease it
really well, because otherwise the dough will stick to the bag and you will
be sad. Scrape the dough out of the mixing bowl into the bag and seal it.
Stick this in the fridge overnight so it can develop some nice flavour.</li>
<li>In the morning, take the bag out of the fridge and let it come up to room
temperature. This will take one to two hours, depending on how warm your
kitchen is.</li>
<li>When the dough is warmed up, sprinkle some flour on the counter and <a href="https://www.youtube.com/watch?v=wx5I5O_RoeI">shape
the dough into a log</a>.</li>
<li>Grease a loaf pan and transfer the dough into the pan. Cover the pan with
plastic wrap and let this rest in a warm place until the loaf is fully
risen. I usually let mine go about an hour and a half to two hours,
depending on how long it rested coming up to room temperature.
<br><br>
<img alt="Dough shaped into a log, beginning to rise in the loaf pan" src="/multigrain-bread/logrise.jpg"></li>
<li>When the dough is just about ready (it should double in size, give or take),
preheat the oven to 350F convection (or 375F conventional bake). Put the
loaf in the oven and bake for 20 minutes.
<br><br>
<img alt="Fully risen loaf" src="/multigrain-bread/risenlog.jpg"></li>
<li>At this point, turn the loaf around and lower the oven temperature to 325F
convection (or 350F conventional bake). Bake for an additional 15 minutes.</li>
<li>Remove the loaf from the oven, pop out of the pan and cool on a cooling
rack. Enjoy once cool... assuming you can wait that long!
<br><br>
<img alt="Golden baked loaf, cooling on a cooling rack" src="/multigrain-bread/loaf.jpg">
<img alt="The loaf, sliced open" src="/multigrain-bread/crumb1.jpg">
<img alt="An irresistable slice of bread, revealing little bubbles and seeds" src="/multigrain-bread/crumb2.jpg"></li>
</ol>Hot chocolate2019-01-08T22:00:00-05:002019-01-08T22:00:00-05:00Elana Hashmantag:hashman.ca,2019-01-08:/hot-chocolate/<p>Sharing some homemade hot chocolate recipes.</p><p>My roommate once told me that “[I] make the best hot chocolate [he's]
ever tasted.” This was a little surprising to me, because my hot
chocolate recipe is not sophisticated, but I guess it <em>does</em> taste pretty good.
😊</p>
<p>Today I will share my secrets with the world.</p>
<h2>Homemade hot chocolate</h2>
<p>Ingredients:</p>
<ul>
<li>1 tsp granulated sugar</li>
<li>1 tbsp unsweetened cocoa powder</li>
<li>splash of vanilla extract (or paste, if you're really fancy)</li>
<li>hot water</li>
<li>10-12oz milk (I like whole milk)</li>
<li>pinch salt</li>
</ul>
<p>Recipe:</p>
<ol>
<li>Mix the sugar, cocoa, and vanilla in a microwave-safe liquid measuring cup.
Add a small amount of hot water and stir until the mixture is fully
combined.</li>
<li>Add milk while stirring until you have about one serving of hot chocolate,
about 10-12oz. Taste.</li>
<li>Adjust any ingredients to taste (more sugar, more cocoa, etc.) and add salt.</li>
<li>Microwave for about 1 minute on high. Stir and taste for a temperature
check.</li>
<li>Microwave for about 30 more seconds or until sufficiently hot.</li>
<li>Pour into a mug or thermos for serving.</li>
<li>Enjoy!</li>
</ol>
<h3>Variations</h3>
<p><strong>Spicy hot chocolate:</strong> substitute ¼ tsp cinnamon for the vanilla. Add a
dash of cayenne.</p>
<p><strong>Peppermint hot chocolate:</strong> substitute a splash of mint extract for the
vanilla.</p>
<p><strong>Non-dairy hot chocolate:</strong> use your favourite non-dairy milk substitute in
place of milk. If it's vanilla-flavoured, leave out the vanilla. If it's
sweetened, you may need to reduce the amount of sugar added.</p>Software Freedom Conservancy: Governance & Federation are the Future2018-12-27T12:00:00-05:002018-12-27T12:00:00-05:00Elana Hashmantag:hashman.ca,2018-12-27:/conservancy-2018/<p>I spoke with Deb Nicholson from the Software Freedom Conservancy about federated free social media, project governance, and how I got my start in free software.</p><p><em>This entry is cross-posted from the <a href="https://sfconservancy.org/blog/2018/dec/27/elanahashman/">Software Freedom Conservancy's blog</a>.</em></p>
<p>This is part of our ongoing series on generous matching donors. Elana is the Queen of Debian Clojure, Empress of Symbol Versioning, Conqueress of ABIs, Python Packaging Authority, ELF Herder and the Winner of this year's Award for most odd, but needful volunteer assistance this year (keeping people from eating pizza on top of the Conservancy tablecloth and so, so much more.) Elana and several other outstanding individuals are joining Private Internet Access and a big anonymous donor in offering a total of <a href="https://sfconservancy.org/news/2018/nov/20/90k-donation-match/">$90K in matching funds to the Conservancy</a> for our continued work to provide both <a href="https://sfconservancy.org/supporter/#growing-projects">support for important free software</a> and a clear voice in favor of <a href="https://sfconservancy.org/blog/2018/dec/11/compliance2/">community-driven licensing</a> and governance practices. </p>
<div style="text-align: center">
<img style="max-height: 25em; max-width: 50%; width: auto;"
alt="Elana hugging a plush cupcake toy."
src="/images/cupcake.jpg" />
</div>
<p><b>Deb:</b> What's the most exciting thing you've seen recently in free software? </p>
<p><b>EH:</b> I'm really excited about a bunch of different emerging free social technologies based on the <a
href="https://activitypub.rocks/">ActivityPub</a> specification that make up the so-called "fediverse." I think <a href="https://joinmastodon.org/">Mastodon</a> might be the most popular—it fills a niche similar to Twitter—but there's also <a href="https://pleroma.social/">Pleroma</a>, which is Mastodon-compatible and a little simpler to deploy, a peer-to-peer replacement for YouTube called <a href="https://joinpeertube.org/en/">PeerTube</a>, and a federated image sharing app you can use instead of Instagram called <a href="https://pixelfed.org/">PixelFed</a>.</p>
<p>Mastodon has demonstrated that when we prioritize user experiences and work together, we can build free software to sustainably run social collectives on independent infrastructure, and even achieve widespread adoption. This is so meaningful to me: the fediverse embodies all of user freedom, consent, autonomy, self-governance, and community in practice. Independent, federated social networks come with the promise of building online social spaces that better reflect the social needs of individuals and their communities in a way that centralized, corporate social media cannot.</p>
<p><b>Deb:</b> What do you wish people knew about Conservancy, that they might not know? </p>
<p><b>EH:</b> Many folks have heard of the wonderful projects that all live together under the Conservancy banner, but I'm not sure if many people are familiar with what it takes to become a Conservancy project!</p>
<p>Conservancy members are required to serve the public interest, develop their software in public, and use a FOSS license. They must also be community-run: a Conservancy project should have a community-elected oversight committee or a minimum of three governing members, so a project can't be run by a single developer, and when members are appointed there can't be more than one employed by a particular company. I think it's really cool that all Conservancy member projects must uphold rigorous standards for community governance. </p>
<p><b>Deb:</b> You work on several well-known free software projects (Debian, Clojure, Python). Do you have any advice for folks who are just starting with free software contributing? </p>
<p><b>EH:</b> It's funny when you put it that way---I got started in free software barely five years ago, and I'm almost surprised by how much I've contributed since then. One of the big barriers for me was believing I could do it at all. Even though I've been using free software since 2006 or so, I didn't really see a lot of people like me in the community, and having lurked in IRC channels for years I was so afraid of trying to
contribute and getting yelled at for messing up something obvious.</p>
<p>In 2013, I decided to attend an open source day at a conference, and met Asheesh Laroia and Carol Willing. They jumped to onboard me with OpenHatch, a project whose mission was to help newcomers get involved in free and open source software. That chance meeting grew into a GSoC internship with OpenHatch the next summer; later, I became a core maintainer of the project until it wound down. Once I had the right skills, it's become harder for me to say no to contributing to new projects than to just contribute! The fear of messing up spectacularly in public is still there, but it doesn't stop me anymore.</p>
<p>Part of OpenHatch's legacy was encouraging projects to identify and publicly label issues that were relatively small (or "bite-sized") and good for beginners. Now, many spiritual successors to OpenHatch exist, including <a href="https://yourfirstpr.github.io/">Your First PR</a> and <a href="https://up-for-grabs.net/">Up For Grabs</a>.</p>
<p><b>Deb:</b> What do you hope to see Conservancy accomplish in the next five years? </p>
<p><b>EH:</b> I think Conservancy is increasingly important for the future of sustainable free software. Recently, we've seen the proliferation of a <a href="https://commonsclause.com/">number</a> of <a href="https://www.cockroachlabs.com/cockroachdb-community-license/">new</a> <a href="https://www.mongodb.com/licensing/server-side-public-license">protectionist</a> <a href="https://www.confluent.io/confluent-community-license">licenses</a>, as corporations become more concerned about their open source projects being monetized by other corporations that don't contribute back. I think corporate sustainability and community sustainability are different things, and I'm concerned that the idea of "sustainability" is being co-opted by companies that define it as seeing greater financial
returns from their open source projects.</p>
<p>Conservancy fights this co-option with a two-pronged strategy: through its software license advocacy, by helping to educate the public on software licenses and by providing license enforcement to member projects to ensure free software remains free; and through its support of member project operations, by enabling the practice of sustainable community free software in providing fiscal sponsorship and administrative services. I think both are very important and I'd love to see Conservancy continue to grow in both areas over the next five years; perhaps it will even accept some fediverse projects as members :)</p>
<p><b>Deb:</b> Anything else you'd like to add? </p>
<p><b>EH:</b> If any of this speaks to you, dear reader, then I'd love to encourage you to <a href="https://sfconservancy.org/supporter/">support Conservancy by making a donation</a>. But if that's not something you're able to do, you can always volunteer with Conservancy or a member project, or help <a href="https://sfconservancy.org/supporter/banner/">spread the word</a> for <a href="https://sfconservancy.org/news/2018/nov/20/90k-donation-match/">this year's fundraiser!</a></p>
<p><i>Photo of Elana Hashman, courtesy of Elana Hashman, all rights reserved.</i></p>I was a podcast guest on The REPL2018-10-26T16:00:00-04:002018-10-26T16:00:00-04:00Elana Hashmantag:hashman.ca,2018-10-26:/the-repl/<p>Daniel Compton hosted me on his Clojure podcast, The REPL, where I talked about Debian, packaging Leiningen, and the Clojure ecosystem in Debian.</p><p>Daniel Compton <a href="https://www.therepl.net/episodes/8/">hosted me on his Clojure podcast, The REPL</a>, where I
talked about Debian, packaging Leiningen, and the Clojure ecosystem in Debian.
It's got everything: spooky abandoned packages, anarchist collectives, software
security policies, and Debian release cycles. Absolutely no shade was thrown at
other distros.</p>
<p>Give it a listen:</p>
<div style="margin: auto; width: 40%">
<audio controls preload="none">
Your browser does not support the <code>audio</code> element.
<source src="https://media.therepl.net/therepl/therepl-008.mp3" type="audio/mpeg" />
</audio>
</div>
<p>Download: <a href="https://media.therepl.net/therepl/therepl-008.mp3">MP3</a></p>
<h2>More Q&A</h2>
<p>After the podcast was published, <a href="http://softwaremaniacs.org/">Ivan Sagalaev</a>
wrote me with a great question about how the different versions of Clojure in
Ubuntu 18.04 work:</p>
<blockquote>
<p>First of all, THANK YOU for making <code>sudo apt install leiningen</code> work! It's so
much better and more consistent than sourcing bash scripts :-)</p>
<p>I have a quick question for you. After installing <code>leiningen</code> and <code>clojure</code>
on Ubuntu 18.04 I see that <code>lein repl</code> starts with clojure 1.8.0, while the
clojure package itself seems to be independent and is version 1.9.0. How is
it possible? I frankly haven't even seen <code>lein</code> downloading its own
clojure.jar...</p>
</blockquote>
<p>I replied:</p>
<blockquote>
<p>Leiningen is "ahead-of-time (AOT) compiled", which is a fancy way of saying
that the Leiningen you download from Ubuntu is pre-built. This means it is
already compiled to Java bytecode, which can be run directly by Java. I ship
the binary Leiningen package as an <a href="https://stackoverflow.com/questions/11947037/what-is-an-uber-jar">"uberjar"</a>, which means all its
dependencies are also included inside the Leiningen jar.</p>
<p><a href="https://salsa.debian.org/clojure-team/leiningen-clojure/blob/2702c4ffd50ce4b691597ad351996e274491b430/leiningen-core/project.clj#L7">Leiningen depends on and is built with Clojure 1.8</a>, so the
Leiningen uberjar in Debian also depends on Clojure 1.8. The <a href="https://packages.ubuntu.com/bionic/clojure">"clojure"</a>
package in 18.04 defaults to installing Clojure 1.9, but that can be
installed simultaneously with the <a href="https://packages.ubuntu.com/bionic/clojure1.8">"clojure1.8"</a> package that
Leiningen depends on in order to build. You can change your default Clojure
to 1.8 using
<a href="https://manpages.debian.org/jessie/dpkg/update-alternatives.8.en.html">alternatives</a>.</p>
<p>When you launch <code>lein repl</code>, by default the Clojure 1.8 runtime that's
compiled in is used. If you run <code>lein repl</code> in the root of a Clojure 1.9
project, Leiningen will download Clojure 1.9 from Clojars and launch a 1.9
repl. If you want to use the Clojure 1.9 shipped with Debian, <a href="https://salsa.debian.org/clojure-team/leiningen-clojure/blob/master/debian/patches/0001-Build-in-offline-mode-with-local-repo.patch">you can change
<code>:local-repo</code> to point at <code>/usr/share/maven-repo</code></a>, but be careful to
also set <code>:offline?</code> to <code>true</code> so you don't try to install things into the
system maven repo by accident.</p>
</blockquote>PyGotham 2018 Talk Resources2018-10-13T00:00:00-04:002018-10-13T00:00:00-04:00Elana Hashmantag:hashman.ca,2018-10-13:/pygotham-2018/<p>Talk resources for "The Black Magic of Python Wheels", presented at PyGotham 2018.</p><p>At PyGotham in 2018, I gave a talk called "The Black Magic of Python Wheels". I
based this talk on my two years of work on <a href="https://github.com/pypa/auditwheel/"><code>auditwheel</code></a> and the
<a href="https://github.com/pypa/manylinux"><code>manylinux</code> platform</a>, hoping to share some dark details of how the
proverbial sausage is made.</p>
<p>It was a fun talk, and I enjoyed the opportunity to wear my Python Packaging
Authority hat:</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true">
<p lang="en" dir="ltr">A very witchy
<a href="https://twitter.com/PyGotham?ref_src=twsrc%5Etfw">@PyGotham</a>
talk from <a href="https://twitter.com/ehashdn?ref_src=twsrc%5Etfw">@ehashdn</a>
about dark ELF magic
<a href="https://t.co/W8JMEVW8GE">pic.twitter.com/W8JMEVW8GE</a>
</p>— Geoffrey Thomas, but spooky (@geofft)
<a href="https://twitter.com/geofft/status/1048645263198474240?ref_src=twsrc%5Etfw">October 6, 2018</a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2>The Black Magic of Python Wheels</h2>
<ul>
<li><a href="https://2018.pygotham.org/talks/understanding-the-black-magic-of-python-wheels/">Talk page, PyGotham website</a></li>
<li><a href="/pygotham-2018/pygotham18-slides.pdf">Talk slides (pdf download)</a></li>
<li><a href="https://pythonwheels.com"><code>pythonwheels.com</code></a></li>
<li><a href="https://github.com/pypa/python-manylinux-demo">Simple <code>manylinux</code> wheelbuilding demo</a></li>
</ul>
<h3>Follow-up readings</h3>
<ul>
<li><a href="http://man7.org/linux/man-pages/man5/elf.5.html">Linux Programmer's Manual: format of Executable and Linkable Format (ELF) Files</a></li>
<li><a href="http://www.man7.org/tlpi/">The Linux Programming Interface</a>: 42.3.2 "Symbol Versioning", pg. 870. <em>(Note: all of
chapter 42 would be a great resource on the topic! This is a great book.)</em></li>
<li><a href="https://www.akkadia.org/drepper/symbol-versioning">ELF Symbol Versioning</a></li>
<li><a href="http://web.archive.org/web/20160107032111/http://www.trevorpounds.com/blog/?p=103">Linking to Older Versioned Symbols</a></li>
<li><a href="https://www.technovelty.org/linux/plt-and-got-the-key-to-code-sharing-and-dynamic-libraries.html">PLT and GOT - the key to code sharing and dynamic libraries</a></li>
</ul>
<h4>All the PEPs referenced in the talk</h4>
<p>In increasing numeric order.</p>
<ul>
<li><a href="https://www.python.org/dev/peps/pep-0376/">PEP 376</a> "Database of Installed Python Distributions"</li>
<li><a href="https://www.python.org/dev/peps/pep-0426/">PEP 426</a> "Metadata for Python Software Packages 2.0"</li>
<li><a href="https://www.python.org/dev/peps/pep-0427/">PEP 427</a> "The Wheel Binary Package Format 1.0"</li>
<li><a href="https://www.python.org/dev/peps/pep-0513/">PEP 513</a> "A Platform Tag for Portable Linux Built Distributions" (aka <code>manylinux1</code>)</li>
<li><a href="https://www.python.org/dev/peps/pep-0571/">PEP 571</a> "The <code>manylinux2010</code> Platform Tag"</li>
</ul>
<h3>Image licensing info</h3>
<ul>
<li><a href="https://plixs.com/photo/3297/tree-cat-silhouette-sunset">Tree Cat Silhouette Sunset</a>: Public Domain (CC0) @besshamiti</li>
<li><a href="https://flic.kr/p/ArW1N9">Happy Halloween! (Costume Dog)</a>: Public Domain (CC0) @milkyfactory</li>
</ul>Report on the Debian Bug Squashing Party2018-06-30T15:30:00-04:002018-06-30T15:30:00-04:00Elana Hashmantag:hashman.ca,2018-06-30:/nyc-bsp-report/<p>The bug squashing party I held in Brooklyn, NY, USA for Debian was a smashing success! Here's what we accomplished.</p><p>Last weekend, six folks (one new contributor and five existing contributors)
attended the bug squashing party I hosted in Brooklyn. We got a lot done, and
attendees demanded that we hold another one soon!</p>
<h2>So when's the next one?</h2>
<p>We agreed that we'd like to see the NYC Debian User Group hold two more BSPs in
the next year: one in October or November of 2018, and another after the Buster
freeze in early 2019, to squash RC bugs. Stay tuned for more details; you may
want to join the <a href="https://lists.debian.org/debian-dug-nyc/">NYC Debian User Group mailing list</a>.</p>
<p>If you know of an organization that would be willing to sponsor the next NYC
BSP (with space, food, and/or dollars), or you're willing to host the next BSP,
please <a href="mailto:ehashman@debian.org?subject=Hosting%20the%20next%20NYC%20Debian%20BSP">reach out</a>.</p>
<h2>What did folks work on?</h2>
<p>We had a list of bugs we collected on an <a href="https://etherpad.net/p/bsp-nyc-0624">etherpad</a>, which I have now
mirrored to gobby (<code>gobby.debian.org/BSP/2018/06-Brooklyn</code>). Attendees updated
the etherpad with their comments and progress. Here's what each of the
participants reported.</p>
<h3>Elana (me)</h3>
<ul>
<li>I filed bugs against two upstream repos (<a href="https://github.com/cemerick/pomegranate/issues/101">pomegranate</a>,
<a href="https://github.com/technomancy/leiningen/issues/2443">leiningen</a>) to update dependencies, which are breaking the Debian
builds due to the <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=891175">libwagon upgrade</a>.</li>
<li>I uploaded new versions of <code>clojure</code> and <code>clojure1.8</code> to fix a bug in how
alternatives were managed: despite <code>/usr/share/java/clojure.jar</code> being owned
by the <code>libclojure${ver}-java</code> binary package, the alternatives group was
being managed by the postinst script in the <code>clojure${ver}</code> package, a
holdover from when the library was not split from the CLI. Unfortunately, the
upgrade is still not passing piuparts in testing, so while I thoroughly
tested local upgrades and they seemed to work okay I'm hoping <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=902445">the failures
didn't break anyone on upgrade</a>. I'll be taking a look at further
refining the preinst script to address the piuparts failure this week.</li>
<li>I fixed the Vcs error on <code>libjava-jdbc-clojure</code> and uploaded new version
0.7.0-2. I also added autopkgtests to the package.</li>
<li>I fixed the Vcs warning on <code>clojure-maven-plugin</code> and uploaded new version
1.7.1-2.</li>
<li>I helped dkg with setting up and running autopkgtests.</li>
</ul>
<h3><a href="https://xana.scru.org/">Clint</a></h3>
<ul>
<li>was hateful in
<a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=901327">#901327</a> (tigervnc)</li>
<li>uploaded a fix for
<a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=902279">#902279</a>
(youtube-dl) to DELAYED/3-day</li>
<li>sent a patch for
<a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=902318">#902318</a>
(monkeysphere)</li>
<li>was less hateful in
<a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=899060">#899060</a>
(monkeysphere)</li>
</ul>
<h3><a href="http://clarete.li/">Lincoln</a></h3>
<p><em>Editor's note:</em> Lincoln was a new Debian contributor and made lots of
progress! Thanks for joining us—we're thrilled with your contributions
🎉 Here's his report.</p>
<ul>
<li>Got my environment setup to work on packages again \o/</li>
<li>Played with
<a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=901318">901318</a> for a
while but didn't really make progress because it seems to be a long
discussion so its bad for a newcomer</li>
<li>Was indeed more successful on the python lands. Opened the following PRs<ul>
<li><a href="https://github.com/Kronuz/pyScss/pull/374">Kronuz/pyScss#374</a></li>
<li><a href="https://github.com/ionelmc/pytest-benchmark/pull/114">ionelmc/pytest-benchmark#114</a></li>
</ul>
</li>
<li>Now working on uploading the patches to python-pyscss &
python-pytest-benchmark packages removing the dependency on python-pathlib.</li>
</ul>
<h3><a href="https://dkg.fifthhorseman.net/blog/">dkg</a></h3>
<ul>
<li>worked on debugging/diagnosing enigmail in preparation for making it
DFSG-free again (see <a href="https://bugs.debian.org/901556">#901556</a>)</li>
<li>got pointers from Lincoln about understanding flow control in
asynchronous javascript for debugging the failing autopkgtest suites</li>
<li>got pointers from Elana on emulating ci.debian.net's autopkgtest
infrastructure so i have a better chance of replicating the failures
seen on that platform</li>
</ul>
<h3>Simon</h3>
<ul>
<li>Moved python-requests-oauthlib to
<a href="https://salsa.debian.org/simonft-guest/python-requests-oauthlib">salsa</a></li>
<li>Updated it to 1.0 (new release), pending a couple final checks.</li>
</ul>
<h3><a href="https://ldpreload.com">Geoffrey</a></h3>
<ul>
<li>Worked with Lincoln on both bugs</li>
<li>Opened <a href="https://bugs.debian.org/902323">#902323</a> about removing
python-pathlib</li>
<li>Working on new pymssql upstream release / restoring it to unstable</li>
</ul>
<h2>By the numbers</h2>
<p>All in all, we completed 6 uploads, worked on 8 bugs, filed 3 bugs, submitted 3
patches or pull requests, and closed 2 bugs. Go us! Thanks to everyone for
contributing to a very productive effort.</p>
<p>See you all next time.</p>Looking back on "Teaching Python: The Hard Parts"2018-06-13T19:00:00-04:002018-06-13T19:00:00-04:00Elana Hashmantag:hashman.ca,2018-06-13:/pycon-2016-reflections/<p>It's been over two years since I presented this talk at PyCon US. Is it still relevant? These are my thoughts on the Python 2 to 3 transition and more.</p><p>One of my goals when writing talks is to produce content with a long shelf
life. Because I'm one of those weird people that prefers to write new talks for
new events, I feel like it'd be a waste of effort if my talks didn't at least
age well. So how do things measure up if I look back on one of my oldest?</p>
<p><a href="/pycon-2016">"Teaching Python: The Hard Parts"</a> remains one of my most popular
talks, despite presenting it just one time at PyCon US 2016. For most of the
past two years, it held steady as a top 10 talk from PyCon 2016 by popularity
on YouTube (although it was recently overtaken by a few hundred views
😳), even when counting it against the keynotes (!), and most of the
<a href="https://www.youtube.com/watch?v=CjYEpVNbM-s#comments">YouTube comments</a> are shockingly nice (!!).</p>
<h2>Well, actually</h2>
<p>Not everyone was a fan. <em>Obviously</em> I should have known better than to tell
instructors they didn't have to use Python 3:</p>
<div style="text-align: center">
<img alt="Matt Williams: Obviously Python 3 should be taught over Python 2. In a few years time 2 will be completely unsupported http://pythonclock.org/"
src="/images/angry_youtube_man.png" />
</div>
<p>Did I give bad advice? Was mentiontioning the usability advantage of better
library support and documentation SEO with Python 2 worth the <em>irreparable
damage I might have done to the community?</em></p>
<p>Matt's not the only one with a chip on his shoulder: the Python 2 → 3
transition has been contentious, and much ink has been spilled on the topic. A
popular author infamously wrote a <a href="https://donotlink.it/OKPM">long screed</a> claiming "PYTHON 3 IS
SUCH A FAILURE IT WILL KILL PYTHON". <strong>Spoiler alert:</strong> Python is still alive,
and the author updated his book for Python 3.</p>
<p>I've now spent a few years writing 2/3 compatible code, and am on the cusp of
dropping Python 2 entirely. I've felt bad for not weighing in on the topic
publicly, because people might have looked to this talk for guidance and
wouldn't know my advice has changed over the past two years.</p>
<h3>A little history</h3>
<p>I wrote this talk based on my experiences teaching Python in the winter and
fall of 2014, and presented it in early 2016. Back then, it wasn't clear if
Python 3 adoption was going to pick up: Hynek wrote <a href="https://hynek.me/articles/python3-2016/">an article about Python 3
adoption</a> a few months before PyCon that contained the ominous
subheading "GLOOM". Python 3 <a href="https://twitter.com/pycharm/status/865659029460209664">only reached a majority mindshare of Python
developers</a> in May 2017!</p>
<p>Why? That's a topic long enough to fill a series of blog posts, but briefly:
the number of breaking changes introduced in the first few releases in Python
3, coupled with the lack of compelling features to incentivize migration led to
slow adoption. <a href="https://alexgaynor.net/2013/dec/30/about-python-3/">Personally, until the Python 3.3 release,</a> I don't think
Python 3 had that balance right to really take off. Version 3.3 was <a href="https://en.wikipedia.org/wiki/History_of_Python#Version_release_dates">released
in fall of 2012</a>. Python 3.4 was only released in early 2014, just
before I mentored at my first set of workshops!</p>
<p>This is a long-winded way to say, "when I gave this talk, it wasn't clear that
telling workshop organizers to teach Python 3 would be good advice, because the
ecosystem wasn't there yet."</p>
<h3>The brave new world</h3>
<p>But this is no longer the case! Python 3 adoption is overtaking Python 2 use,
<a href="https://www.youtube.com/watch?v=66XoCk79kjM">even in the enterprise space</a>. The <a href="https://pythonclock.org/">Python 2 clock</a> keeps on
ticking. Latest releases of Python 3 have compelling features to lure users,
including <a href="https://docs.python.org/3/library/asyncio.html">strong, native concurrency support</a>, <a href="https://docs.python.org/3/reference/lexical_analysis.html#f-strings">formatted
strings</a>, <a href="https://www.python.org/dev/peps/pep-0519/">better cross-system path support</a>, and <a href="https://docs.python.org/3/library/typing.html">type
hints</a>.</p>
<p>This is to say, if I had to pick just one change to make to this talk if I gave
it today, I would tell folks</p>
<h2 style="text-align: center; color: black">USE PYTHON 3! ✨</h2>
<h3>Other updates</h3>
<ul>
<li>The <a href="https://python-packaging.readthedocs.io/en/latest/">documentation for packaging Python</a> is a lot better now.
There have been <a href="https://www.youtube.com/watch?v=qOH-h-EKKac">many</a> <a href="https://www.youtube.com/watch?v=AQsZsgJ30AE">good</a> <a href="https://www.youtube.com/watch?v=ASw2htNYa1E">talks</a> presented on the
subject.</li>
<li>Distributing Python is still hard. There isn't a widely adopted practice for
cross-platform management of compiled dependencies yet, although wheels are
picking up steam. I'm currently working on the <code>manylinux2010</code> update to
address this problem on Linux systems.</li>
</ul>
<h2>Endorsements</h2>
<p>Not to let one YouTube commenter rain on my parade, I am thrilled to say that
some people in the community have written some awfully nice things about my
talk. Thanks to all for doing so—pulling this together really brightened
my day!</p>
<h3>Blog Posts</h3>
<p><strong>Roxanne Johnson</strong> <a href="http://roxjohnson.com/projects/2016/06/19/PyCon-Poster-Next-Steps.html">writes</a>, "Elana Hashman’s PyCon talk on
Teaching Python: The Hard Parts had me nodding so hard I thought I might
actually be headbanging." 😄</p>
<p><strong>Georgia Reh</strong> <a href="http://georgiareh.com/2016/06/post-Pycon">writes</a>, "I am just in love with this talk. Any
one who has seen me speak about teaching git knows I try really hard to not
overload students with information, and Elana has a very clear idea of what a
beginner needs to know when learning python versus what they can learn later."
💖</p>
<h3>Tweets</h3>
<p>When I presented this talk, I was too shy to attach my twitter handle to my
slides, so all these folks tweeted at me by name. Wow!</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true"><p lang="en" dir="ltr">Elana
Hashman's talk on teaching programming has a lot of practical take-aways!
<a href="https://twitter.com/pycon?ref_src=twsrc%5Etfw">@pycon</a> <a
href="https://twitter.com/hashtag/pycon2016?src=hash&ref_src=twsrc%5Etfw">#pycon2016</a></p>—
Susan Tan (@ArcTanSusan) <a
href="https://twitter.com/ArcTanSusan/status/737802727024001032?ref_src=twsrc%5Etfw">June
1, 2016</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true"><p lang="en" dir="ltr">Learned
a bunch from this video: Elana Hashman - Teaching Python: The Hard Parts -
PyCon 2016 :: <a
href="https://t.co/IVeaq0XugJ">https://t.co/IVeaq0XugJ</a></p>— Mita
Williams (@copystar) <a
href="https://twitter.com/copystar/status/738381708542771200?ref_src=twsrc%5Etfw">June
2, 2016</a></blockquote>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true"><p lang="es" dir="ltr">Muy
buenos consejos en la presentación de Elana Hashman - Teaching Python: The Hard
Parts - <a
href="https://twitter.com/hashtag/PyCon2016?src=hash&ref_src=twsrc%5Etfw">#PyCon2016</a>
<a href="https://t.co/5dhNX5KO8y">https://t.co/5dhNX5KO8y</a></p>— José
Carlos García (@quobit) <a
href="https://twitter.com/quobit/status/741674289250881536?ref_src=twsrc%5Etfw">June
11, 2016</a></blockquote>
<blockquote class="twitter-tweet tw-align-center" data-cards="hidden" data-lang="en" data-dnt="true"><p
lang="en" dir="ltr">Elana cool talk! Explaining scope: maybe use analogy of
person having same name as a celebrity <a
href="https://twitter.com/pycon?ref_src=twsrc%5Etfw">@pycon</a> <a
href="https://twitter.com/hashtag/pycon2016?src=hash&ref_src=twsrc%5Etfw">#pycon2016</a>
<a href="https://t.co/LAoGbY4uGO">https://t.co/LAoGbY4uGO</a></p>— robin
chauhan (@robinc) <a
href="https://twitter.com/robinc/status/754753761256480769?ref_src=twsrc%5Etfw">July
17, 2016</a></blockquote>
<blockquote class="twitter-tweet tw-align-center" data-cards="hidden" data-lang="en" data-dnt="true"><p
lang="en" dir="ltr">If you're about to teach a hands-on <a
href="https://twitter.com/hashtag/Python?src=hash&ref_src=twsrc%5Etfw">#Python</a>
workshop/tutorial/class, go watch Elana Hashman's <a
href="https://twitter.com/hashtag/pycon2016?src=hash&ref_src=twsrc%5Etfw">#pycon2016</a>
talk 🎓🐍💖<a
href="https://t.co/xVJ6YBKFtV">https://t.co/xVJ6YBKFtV</a></p>— ✨ Trey
Hunner 🐍 (@treyhunner) <a
href="https://twitter.com/treyhunner/status/796941041547018240?ref_src=twsrc%5Etfw">November
11, 2016</a></blockquote>
<h3>Other</h3>
<p>My talk was included in the <a href="https://github.com/quobit/awesome-python-in-education/blob/master/README.md#conferences-and-videos">"Awesome Python in Education" list</a>.
How cool 😎</p>
<h2>Declaring a small victory</h2>
<p>Writing this post has convinced me that "Teaching Python: The Hard Parts" meets
some arbitrary criteria for "sufficiently forward-thinking." Much of the
content still strikes me as fresh: as an occasional mentor for various
technical workshops, I still keep running into trouble with platform diversity,
the command line, and packaging; the "general advice" section is evergreen for
Python and non-Python workshops alike. So with all that said, here's hoping
that looking back on this talk will keep it alive. <a href="https://www.youtube.com/watch?v=CjYEpVNbM-s">Give it a watch</a>
if you haven't seen it before!</p>
<p>If you like what you see and you're interested in checking out my speaking
portfolio or would like to invite me to speak at your conference or event,
do check out my <a href="/talks">talks</a> page.</p>I'm hosting a small Debian BSP in Brooklyn2018-06-02T15:20:00-04:002018-06-02T15:20:00-04:00Elana Hashmantag:hashman.ca,2018-06-02:/nyc-bsp/<p>I'm hosting a Debian BSP on Sunday, June 24th at 61 Local in Brooklyn, NY, USA from 3pm to 8pm.</p><p>The time has come for NYC Debian folks to gather. I've bravely volunteered to
host a <a href="https://wiki.debian.org/BSP/2018/06/us/NYC">local bug squashing party</a>
(or BSP) in late June.</p>
<h3>Details</h3>
<ul>
<li><strong>Venue:</strong> <a href="https://www.openstreetmap.org/node/3575097893">61 Local</a>: 61
Bergen St., Brooklyn, NY, USA</li>
<li><strong>More about the venue:</strong> <a href="http://61local.com/">website</a>, good vegetarian
options available</li>
<li><strong>Date:</strong> Sunday, June 24, 2018</li>
<li><strong>Start:</strong> 3pm</li>
<li><strong>End:</strong> 8pm or so</li>
<li><strong>RSVP:</strong> <a href="https://ti.to/nyc-dug/june-bsp">Please RSVP! Click here</a></li>
</ul>
<h2>I'm an existing contributor, what should I work on?</h2>
<p>The focus of this BSP is to give existing contributors some dedicated time to
work on their projects. I don't have a specific outcome in mind. I do not plan
on tagging bugs specifically for the BSP, but that shouldn't stop you from
doing so if you want to.</p>
<p>Personally, I am going to spend some time on fixing the alternatives logic in
the <code>clojure</code> and <code>clojure1.8</code> packages.</p>
<p>If you don't really have a project you want to work on, but you're interested
in helping mentor new contributors that show up, please <a href="mailto:ehashman@debian.org">get in
touch</a>.</p>
<h2>I'm a new contributor and want to join but I have no idea what I'm doing!</h2>
<p>At some point, that was all of us!</p>
<p>Even though this BSP is aimed at existing contributors, you are welcome to
attend! We'll have a dedicated mentor available to coordinate and help out new
contributors.</p>
<p>If you've never contributed to Debian before, I recommend you check out <a href="https://www.debian.org/intro/help">"How
can you help Debian?"</a> and <a href="https://wiki.debian.org/BSP/BeginnersHOWTO">the beginner's
HOWTO for BSPs</a> in advance of the
BSP. I also wrote a
<a href="https://wiki.debian.org/Clojure/PackagingTutorial">tutorial</a> and <a href="/debian-build-tools">blog
post</a> on packaging that might help. Remember, you <strong>don't
have to code or build packages</strong> to make valuable contributions!</p>
<h2>See you there!</h2>
<p>Happy hacking.</p>A tale of three Debian build tools2018-04-03T00:00:00-04:002018-04-03T00:00:00-04:00Elana Hashmantag:hashman.ca,2018-04-03:/debian-build-tools/<p>An overview of my Debian workflow, and how I use three different build tools to get the job done.</p><p>Many people have asked me about my Debian workflow. Which is funny, because
it's hard to believe that when you use three different build tools that you're
doing it right, but I <em>have</em> figured out a process that works for me. I use
<code>git-buildpackage</code> (<code>gbp</code>), <code>sbuild</code>, and <code>pbuilder</code>, each for different
purposes. Let me describe why and how I use each, and the possible downsides of
each tool.</p>
<p><strong>Note:</strong> This blog post is aimed at people already familiar with Debian
packaging, particularly using <code>Vcs-Git</code>. If you'd like to learn more about the
basics of Debian packaging, I recommend you check out my <a href="https://wiki.debian.org/Clojure/PackagingTutorial">Clojure Packaging
Tutorial</a> and my <a href="/clojuresync/">talk about
packaging Leiningen</a>.</p>
<h2><code>git-buildpackage</code>: the workhorse</h2>
<p>I use <a href="http://honk.sigxcpu.org/projects/git-buildpackage/manual-html/"><code>git-buildpackage</code></a> (aka <code>gbp</code>) to do the majority of my package
builds. Because it runs by default without any special sandboxing from your
base system, <code>gbp</code> builds things <em>fast</em>—much faster than other build
tools—and as such, it's a great tool if you are iterating quickly to work
bugs out of a build.</p>
<p>I usually invoke <code>gbp</code> like this:</p>
<div class="highlight"><pre><span></span><code>gbp<span class="w"> </span>buildpackage<span class="w"> </span>-uc<span class="w"> </span>-us
</code></pre></div>
<p>The flags ensure that 1) we don't sign the .changes file generated (<code>-uc</code>) 2)
we don't sign the .dsc file (<code>-us</code>), since typically I only want to sign build
artifacts right before upload.</p>
<p>Other handy flags include</p>
<ul>
<li><code>--git-ignore-new</code>, which proceeds with a build even when you have an unclean
working tree</li>
<li><code>--git-ignore-branch</code>, to build when you've checked out a branch that's not
<code>master</code></li>
<li><code>--git-pristine-tar</code>, to automatically check out and build with the
corresponding pristine tar checked into the repository</li>
</ul>
<p>Typically, if I use <code>gbp</code> to build binary packages, I will only do so in the
confines of an updated, minimal sid LXC container, in order to reduce the risk
of contaminating my build with stuff that's not available in a clean build
environment.</p>
<p><code>gbp</code> tastes great with <a href="https://manpages.debian.org/stretch/devscripts/sadt.1.en.html"><code>sadt</code></a>, a script in <code>devscripts</code> that runs your
package's <a href="https://people.debian.org/~mpitt/autopkgtest/README.package-tests.html">autopkgtests</a> directly on your base system without
root. You will need to install your test package and any dependencies before
you can run <code>sadt</code>, but it requires much less setup and infrastructure than
<code>autopkgtest</code> does.</p>
<div style="text-align:center"><strong>***</strong></div>
<p>So while <code>gbp</code> is awesome for the majority of my development workflow, I don't
rely on the output of <code>gbp</code> when I want to upload a package, however clean I
think my build LXC is. For uploads, I still use <code>gbp</code>, but exclusively to
perform source-only (<code>-S</code>) builds:</p>
<div class="highlight"><pre><span></span><code>gbp<span class="w"> </span>buildpackage<span class="w"> </span>-S<span class="w"> </span>-uc<span class="w"> </span>-us
</code></pre></div>
<p>Sometimes I may also need to pass the <code>-d</code> flag when building source packages
to ignore installed build dependencies. By default, <code>gbp</code> won't proceed with a
build when build dependencies are not installed, but when a said dependency is
only needed for building the binary (and not the source) package, we can safely override this check.</p>
<p>Typically, when I'm uploading an update to a package, I'll just build the
source package with <code>gbp</code> as above, sign the .changes and .dsc files, and
complete a source-only upload so the buildds can handle all the build details.</p>
<h2><code>sbuild</code>: the gating build</h2>
<p>"But wait!" you undoubtedly object. "How do you know your source package isn't
full of garbage?!" That's a great question, because... I don't. So it behooves
me to test it. And since the buildds use <a href="https://wiki.debian.org/sbuild"><code>sbuild</code></a>, why not use that
for my own QA?</p>
<p>Setting up <code>sbuild</code> is a giant pain. You need a machine you have root access
on, which wasn't the case for <code>git-buildpackage</code>.</p>
<p>To use <code>sbuild</code> without sudo, you'll need to add your user to the <code>sbuild</code>
group:</p>
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>sbuild-adduser<span class="w"> </span>myusername
</code></pre></div>
<p>And create a schroot for builds:</p>
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>sbuild-createchroot<span class="w"> </span>unstable<span class="w"> </span>/srv/chroot/unstable-amd64-sbuild<span class="w"> </span>http://deb.debian.org/debian
</code></pre></div>
<p>If you ever need to update your <code>sbuild</code> schroot (you will), you can run:</p>
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>sbuild-update<span class="w"> </span>-udcar<span class="w"> </span>unstable-amd64-sbuild
</code></pre></div>
<p>I remember these flags by pronouncing them as one word, "ud-car", which sounds
absurd and is hence memorable. I can never remember the name of my schroot, but
I can look that up by running <code>ls /srv/chroot</code>.</p>
<p>Okay, time to build. Once we have our source package from <code>gbp</code>, we should have
a .dsc file to pass to <code>sbuild</code>. We can build like so:</p>
<div class="highlight"><pre><span></span><code>sbuild<span class="w"> </span>mypackage_1.0-1.dsc<span class="w"> </span>-d<span class="w"> </span>unstable<span class="w"> </span>-A<span class="w"> </span>--run-autopkgtest
</code></pre></div>
<p>We specify the target distribution with <code>-d unstable</code>, and <code>-A</code> ensures that
our "arch: all" package will actually build (not necessary if your package
targets specific architectures). To run the autopkgtests after a successful
build, we pass <code>--run-autopkgtest</code>.</p>
<p>I don't like typing very much so I stick all these parameters in an <code>.sbuildrc</code>
in my home directory. You can reference or copy the one in
<code>/usr/share/doc/sbuild/examples/example.sbuildrc</code> because it's a Perl script
that ends in <code>1;</code>.</p>
<p>I add</p>
<div class="highlight"><pre><span></span><code><span class="nv">$distribution</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">'unstable'</span><span class="p">;</span>
<span class="nv">$build_arch_all</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="c1"># Optionally enable running autopkgtests</span>
<span class="c1"># $run_autopkgtest = 1;</span>
</code></pre></div>
<p>so I don't have to type these in all the time. I don't enable autopkgtests by
default because they prompt for a sudo password midway through the build (but
perhaps that won't bother you, so feel free to uncomment those lines). Once we
have our <code>~/.sbuildrc</code> created, then we can just run</p>
<div class="highlight"><pre><span></span><code>sbuild<span class="w"> </span>mypackage_1.0-1.dsc
</code></pre></div>
<p>Much better!</p>
<p>After my package successfully builds and tests, I take a quick look at the
changes, build environment, and package contents. <code>sbuild</code> automatically prints
these out, which is very convenient. If everything looks okay, I will sign the
source package with <a href="https://manpages.debian.org/stretch/devscripts/debsign.1.en.html"><code>debsign</code></a> and upload it with <a href="https://manpages.debian.org/stretch/dput-ng/dput.1.en.html"><code>dput</code></a>
(from <code>dput-ng</code>).</p>
<h2><code>pbuilder</code>: I need to upload binaries!</h2>
<p>One irritation I have with <code>sbuild</code> is I can never figure out the right flags
to get the right build artifacts to do a binary upload. Its defaults are too
minimal for sending to NEW without some additional fancy incantations (it
doesn't include the package tarball, only the buildinfo and produced .deb), and
I have a hard enough time remembering the flags that I listed above. Remember,
this is what the manpage for <code>sbuild</code> looks like:</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true">
<p lang="en" dir="ltr">MANPAGE OF THE DAY: sbuild
<a href="https://t.co/e7nwB5PUUZ">https://t.co/e7nwB5PUUZ</a>
<br><br>
...this was a little traumatizing tbh
<a href="https://t.co/zSSEbe4ROH">pic.twitter.com/zSSEbe4ROH</a>
</p>
— e. hashman (@ehashdn)
<a href="https://twitter.com/ehashdn/status/944771961367998464?ref_src=twsrc%5Etfw">December 24, 2017</a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>So when I have to upload a NEW package, I usually use <a href="https://pbuilder.alioth.debian.org/"><code>pbuilder</code></a>.</p>
<p>There are two ways to invoke <code>pbuilder</code>. The first is the easiest, but "not
recommended" for uploads by the manpage: simply navigate to the root of the
repository you want to build and run <code>pdebuild</code>.</p>
<div class="highlight"><pre><span></span><code>pdebuild
</code></pre></div>
<p>Wow, that's the simplest thing we've run yet! Why don't we run it all the
time?! Well, because of the way <code>pbuilder</code> sets up its filesystem, it can be
slower than <code>sbuild</code>, so I've moved it out of my development workflow. It also
requires my sudo password, and as I mentioned earlier, I don't particularly
like having to enter that mid-build.</p>
<p>The second way I usually apply when I need to upload a build: invoking
<code>pbuilder</code> directly. Like with <code>sbuild</code>, we need to provide it a .dsc, so we
should build a source package first. However, <code>pbuilder</code> is smarter than
<code>sbuild</code> and doesn't need me to give it architectures and target distros and
whatnot, so there is significantly less headache if I haven't tweaked a
personal configuration. With <code>pbuilder</code>, I can run</p>
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>pbuilder<span class="w"> </span>build<span class="w"> </span>mypackage_1.0-1.dsc
</code></pre></div>
<p>and I get a package!</p>
<p>One of the annoying things about <code>pbuilder</code> is that it doesn't output files in
my current build directory. Instead, by default, it places build artifacts
inside <code>/var/cache/pbuilder/result</code>. So I always have to remember to copy
things out of there before I upload.</p>
<p>Also, <code>pbuilder</code> doesn't print out some build information that I should check
over before uploading, so I have to do that manually with <a href="https://manpages.debian.org/stretch/dpkg/dpkg.1.en.html"><code>dpkg</code></a>:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Print out package information, including dependencies</span>
dpkg<span class="w"> </span>-I<span class="w"> </span>libmypackage_1.0-1_all.deb
<span class="c1"># List all contents of the package</span>
dpkg<span class="w"> </span>-c<span class="w"> </span>libmypackage_1.0-1_all.deb
</code></pre></div>
<p>Now I can go ahead and sign my built .changes file and perform an upload!</p>
<h2>In summary</h2>
<p>Here are what I'd say the pros and cons of each of these three build tools I
run are.</p>
<h3><code>git-buildpackage</code></h3>
<p>Pros:</p>
<ul>
<li>Super speedy (gotta go fast)</li>
<li>Uses version control</li>
<li>No rootz</li>
</ul>
<p>Cons:</p>
<ul>
<li>Way too much typing</li>
<li>Constantly have to run <code>d/rules clean</code> or pass <code>--git-ignore-new</code></li>
<li>Can't produce production build artifacts</li>
</ul>
<h3><code>sbuild</code></h3>
<p>Pros:</p>
<ul>
<li>Supposedly "fast"</li>
<li>Prints helpful information once the build is complete</li>
<li>Runs autopkgtests with no marginal effort</li>
<li>Only needs root like every third time I run it</li>
<li>Conforms with the buildds</li>
</ul>
<p>Cons:</p>
<ul>
<li>Setup is way too complicated</li>
<li>Manpage is terrifying</li>
<li>Doesn't actually give me the build artifacts I want</li>
</ul>
<h3><code>pbuilder</code></h3>
<p>Pros:</p>
<ul>
<li>The least typing!!!</li>
<li>Gets me all the build artifacts I want</li>
<li>Not the reason I get rejected by FTP Master</li>
</ul>
<p>Cons:</p>
<ul>
<li>Slow</li>
<li>Sticks build artifacts into /var/run/wheretheheck</li>
<li>People will yell at you for not using <code>sbuild</code></li>
</ul>
<h3>cowbuilder, qemubuilder, whalebuilder, mylittlepersonalbuilder, etc.</h3>
<p>Pros:</p>
<ul>
<li>I don't use these.</li>
</ul>
<p>Cons:</p>
<ul>
<li>I don't use these.</li>
</ul>
<p>Hope you enjoyed the tour. Happy building!</p>
<h2>References</h2>
<ul>
<li><code>git-buildpackage</code> <a href="http://honk.sigxcpu.org/projects/git-buildpackage/manual-html/">homepage</a></li>
<li><code>sadt</code> <a href="https://manpages.debian.org/stretch/devscripts/sadt.1.en.html">manpage</a></li>
<li><code>autopkgtest</code> <a href="https://people.debian.org/~mpitt/autopkgtest/README.package-tests.html">homepage</a></li>
<li><code>sbuild</code> <a href="https://wiki.debian.org/sbuild">wiki page</a></li>
<li><code>debsign</code> <a href="https://manpages.debian.org/stretch/devscripts/debsign.1.en.html">manpage</a></li>
<li><code>dput</code> <a href="https://manpages.debian.org/stretch/dput-ng/dput.1.en.html">manpage</a></li>
<li><code>pbuilder</code> <a href="https://pbuilder.alioth.debian.org/">homepage</a></li>
<li><code>dpkg</code> <a href="https://manpages.debian.org/stretch/dpkg/dpkg.1.en.html">manpage</a></li>
</ul>ClojureSYNC Talk Resources2018-03-17T00:00:00-04:002018-03-17T00:00:00-04:00Elana Hashmantag:hashman.ca,2018-03-17:/clojuresync/<p>Talk resources for "<code>apt-get install leiningen</code>: Bootstrapping the Clojure Ecosystem for Debian", presented at the inaugural ClojureSYNC.</p><p>At the inaugural <a href="https://clojuresync.com/">ClojureSYNC</a> in 2018, I gave a talk called "<code>apt-get
install leiningen</code>: Bootstrapping the Clojure Ecosystem for Debian". This was
the culmination of the year of work I put into packaging Leiningen 2.x and
other Clojure software for Debian.</p>
<p>It was incredibly exciting to present there, and Eric ran a fabulous
conference! I wish more tech conferences would send me to New Orleans.</p>
<h2><code>apt-get install leiningen</code>: Bootstrapping the Clojure Ecosystem for Debian</h2>
<ul>
<li><a href="https://clojuresync.com/elana-hashman/">Talk page, ClojureSYNC website</a></li>
<li>Talk video: posted at the link above</li>
<li><a href="/clojuresync/clojuresync-ehashman.pdf">Talk slides (pdf download)</a></li>
<li><a href="https://qa.debian.org/developer.php?login=debian%40hashman.ca+ehashman&comaint=yes">All the packages I maintain</a></li>
<li><a href="https://salsa.debian.org/clojure-team">Clojure Team's GitLab repository</a></li>
<li><a href="https://wiki.debian.org/Clojure">Debian Clojure Wiki</a></li>
<li><a href="https://wiki.debian.org/Clojure/PackagingTutorial">Debian Clojure Packaging Tutorial</a></li>
</ul>
<h3>Follow-up readings</h3>
<ul>
<li><a href="https://danluu.com/web-bloat/">How web bloat affects people with slow connections</a>, by Dan Luu</li>
<li><a href="https://www.ashedryden.com/blog/the-ethics-of-unpaid-labor-and-the-oss-community">The Ethics of Unpaid Labor and the OSS Community</a>, by Ashe Dryden</li>
<li><a href="https://www.debian.org/social_contract">Debian Social Contract</a></li>
<li><a href="https://www.debian.org/social_contract#guidelines">Debian Free Software Guidelines</a></li>
<li><a href="https://lwn.net/Articles/201488/">Debian Dunc-Tank Controversy</a> via LWN</li>
</ul>
<h3>Image licensing info</h3>
<p>"There's no NEW queue for talk slides." – lamby, Debian Project Leader</p>
<ul>
<li>Debian logo, copyright 1999 <a href="https://www.debian.org/logos/">Software in the Public Interest,
Inc.</a> used under the <a href="http://creativecommons.org/licenses/by-sa/3.0/">Attribution-ShareAlike 3.0 Unported
License</a>.</li>
<li>Images on "The '90s" and "Walnut Creek GIFs Galore CDROM" slides were
obtained from <a href="https://archive.org/details/GifsGalore_Aug92">The Internet Archive</a> and included under their <a href="https://archive.org/about/terms.php">Terms of
Use</a>. It is believed the inclusion of these images, for
illustration of the history of computing and file distribution, constitutes
fair use under US copyright law.</li>
<li><a href="https://www.gnu.org/graphics/bwcartoon.html">Dynamic Duo: the Gnu and the Penguin in flight</a> in colour, used under
the <a href="https://www.gnu.org/licenses/fdl-1.3.en.html">GNU Free Documentation License, v1.3</a>.</li>
<li><a href="https://en.wikipedia.org/wiki/File:Yggdrasil-linux-summer-94.JPG">Yggdrasil Computing Plug and Play Linux</a> was obtained from
Wikipedia. It is believed the inclusion of this image, for illustration of
the history of early Linux distributions, constitutes fair use under US
copyright law.</li>
<li><a href="https://commons.wikimedia.org/wiki/File:DebianFamilyTree1210.svg">Debian family tree</a>, authors Andreas Lundqvist, Donjan Rodic,
modified by <a href="https://commons.wikimedia.org/wiki/User:Michaeldsuarez">Michaeldsuarez</a>, used under the
<a href="https://www.gnu.org/licenses/fdl-1.3.en.html">GNU Free Documentation License, v1.3</a>.</li>
</ul>Stop streaming music from YouTube with this one weird trick2018-03-07T00:00:00-05:002018-03-07T00:00:00-05:00Elana Hashmantag:hashman.ca,2018-03-07:/youtube-dl/<p>Learn to use youtube-dl and ffmpeg to download and process audio you'd normally stream! 🎶</p><p>Having grown up on the internet long before the average connection speed made
music streaming services viable, streaming has always struck me as wasteful.
And I know that doesn't make much sense—it's not like there's a limited
amount of bandwidth to go around! But if I'm going to listen to the same audio
file five times, why not just download it once and listen to it forever?
Particularly if I want to listen to it while airborne and avoid the horrors of
plane wifi. Or if I want to remove NSFW graphics that seem to frequently
accompany mixes I enjoy.</p>
<h2><code>youtube-dl</code> to the rescue</h2>
<p>Luckily, at least as far as YouTube audio is concerned, there is plenty of free
software available to help with this! I like to fetch and process music from
YouTube using <a href="http://rg3.github.io/youtube-dl/">youtube-dl</a> and
<a href="https://ffmpeg.org/">ffmpeg</a>. Both are packaged and available in Debian if you
like to <code>apt install</code> things:</p>
<ul>
<li><a href="https://packages.debian.org/stretch/youtube-dl">youtube-dl package</a></li>
<li><a href="https://packages.debian.org/stretch/ffmpeg">ffmpeg package</a></li>
</ul>
<h2>Saving audio files from YouTube</h2>
<p>Well, let's suppose you want to download <a href="https://www.youtube.com/watch?v=8B4guKLlbVU">some eurobeat</a>. <code>youtube-dl</code> can
help. The <code>-x</code> flag tells <code>youtube-dl</code> to skip downloading video and to only
fetch audio.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>youtube-dl<span class="w"> </span>-x<span class="w"> </span>https://www.youtube.com/watch?v<span class="o">=</span>8B4guKLlbVU
<span class="o">[</span>youtube<span class="o">]</span><span class="w"> </span>8B4guKLlbVU:<span class="w"> </span>Downloading<span class="w"> </span>webpage
<span class="o">[</span>youtube<span class="o">]</span><span class="w"> </span>8B4guKLlbVU:<span class="w"> </span>Downloading<span class="w"> </span>video<span class="w"> </span>info<span class="w"> </span>webpage
<span class="o">[</span>youtube<span class="o">]</span><span class="w"> </span>8B4guKLlbVU:<span class="w"> </span>Extracting<span class="w"> </span>video<span class="w"> </span>information
<span class="o">[</span>youtube<span class="o">]</span><span class="w"> </span>8B4guKLlbVU:<span class="w"> </span>Downloading<span class="w"> </span>js<span class="w"> </span>player<span class="w"> </span>vflGUPF-i
<span class="o">[</span>download<span class="o">]</span><span class="w"> </span>Destination:<span class="w"> </span>SUPER<span class="w"> </span>EUROBEAT<span class="w"> </span>MIX-8B4guKLlbVU.webm
<span class="o">[</span>download<span class="o">]</span><span class="w"> </span><span class="m">100</span>%<span class="w"> </span>of<span class="w"> </span><span class="m">60</span>.68MiB<span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">20</span>:57
Deleting<span class="w"> </span>original<span class="w"> </span>file<span class="w"> </span>SUPER<span class="w"> </span>EUROBEAT<span class="w"> </span>MIX-8B4guKLlbVU.webm<span class="w"> </span><span class="o">(</span>pass<span class="w"> </span>-k<span class="w"> </span>to<span class="w"> </span>keep<span class="o">)</span>
</code></pre></div>
<p>YouTube sometimes throttles connections to approximate real-time buffer rates,
so the download could take a while. If you need to interrupt the download for
some reason, you can use <code>SIGINT</code> (<code>Ctrl+C</code>) to stop it. If you run
<code>youtube-dl</code> again, it's smart enough to resume the download where you left
off. </p>
<p>Once the download is complete, there's not much more to do. It will be saved
with the appropriate file extension so you can determine what audio codec it
uses. You might want to rename the file:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>mv<span class="w"> </span>SUPER<span class="se">\ </span>EUROBEAT<span class="se">\ </span>MIX-8B4guKLlbVU.ogg<span class="w"> </span>super_eurobeat_mix.ogg
</code></pre></div>
<p>Now we can enjoy many plays of our <code>super_eurobeat_mix.ogg</code> file!
🚗🎶</p>
<h2>Re-encoding audio to another format</h2>
<p>Suppose that I have a really old MP3 player I'd like to put this file on, and
it doesn't support the OGG Vorbis format. That's not a problem; we can use
<code>ffmpeg</code> to re-encode the audio.</p>
<p>The <code>-i</code> flag to <code>ffmpeg</code> specifies an input file. <code>-acodec mp3</code> says that we
want to use the mp3 codec to re-encode our audio. The last positional argument,
<code>super_eurobeat_mix.mp3</code>, is the name of the file we want to output.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>ffmpeg<span class="w"> </span>-i<span class="w"> </span>super_eurobeat_mix.ogg<span class="w"> </span>-acodec<span class="w"> </span>mp3<span class="w"> </span>super_eurobeat_mix.mp3
ffmpeg<span class="w"> </span>version<span class="w"> </span><span class="m">3</span>.4.2-1+b1<span class="w"> </span>Copyright<span class="w"> </span><span class="o">(</span>c<span class="o">)</span><span class="w"> </span><span class="m">2000</span>-2018<span class="w"> </span>the<span class="w"> </span>FFmpeg<span class="w"> </span>developers
<span class="w"> </span>built<span class="w"> </span>with<span class="w"> </span>gcc<span class="w"> </span><span class="m">7</span><span class="w"> </span><span class="o">(</span>Debian<span class="w"> </span><span class="m">7</span>.3.0-4<span class="o">)</span>
<span class="w"> </span>configuration:<span class="w"> </span>--prefix<span class="o">=</span>/usr<span class="w"> </span>--extra-version<span class="o">=</span><span class="m">1</span>+b1<span class="w"> </span>--toolchain<span class="o">=</span>hardened
<span class="w"> </span>--libdir<span class="o">=</span>/usr/lib/x86_64-linux-gnu<span class="w"> </span>--incdir<span class="o">=</span>/usr/include/x86_64-linux-gnu
<span class="w"> </span>--enable-gpl<span class="w"> </span>--disable-stripping<span class="w"> </span>--enable-avresample<span class="w"> </span>--enable-avisynth
<span class="w"> </span>--enable-gnutls<span class="w"> </span>--enable-ladspa<span class="w"> </span>--enable-libass<span class="w"> </span>--enable-libbluray
<span class="w"> </span>--enable-libbs2b<span class="w"> </span>--enable-libcaca<span class="w"> </span>--enable-libcdio<span class="w"> </span>--enable-libflite
<span class="w"> </span>--enable-libfontconfig<span class="w"> </span>--enable-libfreetype<span class="w"> </span>--enable-libfribidi<span class="w"> </span>--enable-libgme
<span class="w"> </span>--enable-libgsm<span class="w"> </span>--enable-libmp3lame<span class="w"> </span>--enable-libmysofa<span class="w"> </span>--enable-libopenjpeg
<span class="w"> </span>--enable-libopenmpt<span class="w"> </span>--enable-libopus<span class="w"> </span>--enable-libpulse<span class="w"> </span>--enable-librubberband
<span class="w"> </span>--enable-librsvg<span class="w"> </span>--enable-libshine<span class="w"> </span>--enable-libsnappy<span class="w"> </span>--enable-libsoxr
<span class="w"> </span>--enable-libspeex<span class="w"> </span>--enable-libssh<span class="w"> </span>--enable-libtheora<span class="w"> </span>--enable-libtwolame
<span class="w"> </span>--enable-libvorbis<span class="w"> </span>--enable-libvpx<span class="w"> </span>--enable-libwavpack<span class="w"> </span>--enable-libwebp
<span class="w"> </span>--enable-libx265<span class="w"> </span>--enable-libxml2<span class="w"> </span>--enable-libxvid<span class="w"> </span>--enable-libzmq
<span class="w"> </span>--enable-libzvbi<span class="w"> </span>--enable-omx<span class="w"> </span>--enable-openal<span class="w"> </span>--enable-opengl<span class="w"> </span>--enable-sdl2
<span class="w"> </span>--enable-libdc1394<span class="w"> </span>--enable-libdrm<span class="w"> </span>--enable-libiec61883<span class="w"> </span>--enable-chromaprint
<span class="w"> </span>--enable-frei0r<span class="w"> </span>--enable-libopencv<span class="w"> </span>--enable-libx264<span class="w"> </span>--enable-shared
<span class="w"> </span>libavutil<span class="w"> </span><span class="m">55</span>.<span class="w"> </span><span class="m">78</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">55</span>.<span class="w"> </span><span class="m">78</span>.100
<span class="w"> </span>libavcodec<span class="w"> </span><span class="m">57</span>.107.100<span class="w"> </span>/<span class="w"> </span><span class="m">57</span>.107.100
<span class="w"> </span>libavformat<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">83</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">83</span>.100
<span class="w"> </span>libavdevice<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">10</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">10</span>.100
<span class="w"> </span>libavfilter<span class="w"> </span><span class="m">6</span>.107.100<span class="w"> </span>/<span class="w"> </span><span class="m">6</span>.107.100
<span class="w"> </span>libavresample<span class="w"> </span><span class="m">3</span>.<span class="w"> </span><span class="m">7</span>.<span class="w"> </span><span class="m">0</span><span class="w"> </span>/<span class="w"> </span><span class="m">3</span>.<span class="w"> </span><span class="m">7</span>.<span class="w"> </span><span class="m">0</span>
<span class="w"> </span>libswscale<span class="w"> </span><span class="m">4</span>.<span class="w"> </span><span class="m">8</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">4</span>.<span class="w"> </span><span class="m">8</span>.100
<span class="w"> </span>libswresample<span class="w"> </span><span class="m">2</span>.<span class="w"> </span><span class="m">9</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">2</span>.<span class="w"> </span><span class="m">9</span>.100
<span class="w"> </span>libpostproc<span class="w"> </span><span class="m">54</span>.<span class="w"> </span><span class="m">7</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">54</span>.<span class="w"> </span><span class="m">7</span>.100
Input<span class="w"> </span><span class="c1">#0, ogg, from 'super_eurobeat_mix.ogg':</span>
<span class="w"> </span>Duration:<span class="w"> </span><span class="m">01</span>:06:35.57,<span class="w"> </span>start:<span class="w"> </span><span class="m">0</span>.000000,<span class="w"> </span>bitrate:<span class="w"> </span><span class="m">124</span><span class="w"> </span>kb/s
<span class="w"> </span>Stream<span class="w"> </span><span class="c1">#0:0(eng): Audio: vorbis, 44100 Hz, stereo, fltp, 128 kb/s</span>
<span class="w"> </span>Metadata:
<span class="w"> </span>LANGUAGE<span class="w"> </span>:<span class="w"> </span>eng
<span class="w"> </span>ENCODER<span class="w"> </span>:<span class="w"> </span>Lavf57.83.100
Stream<span class="w"> </span>mapping:
<span class="w"> </span>Stream<span class="w"> </span><span class="c1">#0:0 -> #0:0 (vorbis (native) -> mp3 (libmp3lame))</span>
Press<span class="w"> </span><span class="o">[</span>q<span class="o">]</span><span class="w"> </span>to<span class="w"> </span>stop,<span class="w"> </span><span class="o">[</span>?<span class="o">]</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nb">help</span>
Output<span class="w"> </span><span class="c1">#0, mp3, to 'super_eurobeat_mix.mp3':</span>
<span class="w"> </span>Metadata:
<span class="w"> </span>TSSE<span class="w"> </span>:<span class="w"> </span>Lavf57.83.100
<span class="w"> </span>Stream<span class="w"> </span><span class="c1">#0:0(eng): Audio: mp3 (libmp3lame), 44100 Hz, stereo, fltp</span>
<span class="w"> </span>Metadata:
<span class="w"> </span>LANGUAGE<span class="w"> </span>:<span class="w"> </span>eng
<span class="w"> </span>encoder<span class="w"> </span>:<span class="w"> </span>Lavc57.107.100<span class="w"> </span>libmp3lame
<span class="nv">size</span><span class="o">=</span><span class="w"> </span>62432kB<span class="w"> </span><span class="nv">time</span><span class="o">=</span><span class="m">01</span>:06:35.58<span class="w"> </span><span class="nv">bitrate</span><span class="o">=</span><span class="w"> </span><span class="m">128</span>.0kbits/s<span class="w"> </span><span class="nv">speed</span><span class="o">=</span><span class="m">22</span>.4x<span class="w"> </span>
video:0kB<span class="w"> </span>audio:62431kB<span class="w"> </span>subtitle:0kB<span class="w"> </span>other<span class="w"> </span>streams:0kB<span class="w"> </span>global<span class="w"> </span>headers:0kB
muxing<span class="w"> </span>overhead:<span class="w"> </span><span class="m">0</span>.000396%
</code></pre></div>
<p>Voila! We now have a <code>super_eurobeat_mix.mp3</code> file we can copy to our janky old
MP3 player.</p>
<h2>Extracting audio from an existing video file</h2>
<p>Sometimes I accidentally forget to pass the <code>-x</code> flag to <code>youtube-dl</code>,
and get a video file instead of an audio track. Oops.</p>
<p>But that's okay! Extracting the audio track from the video file with <code>ffmpeg</code>
is just a couple of commands away.</p>
<p>First, we should determine the encoding of the audio track. The combined video
file is a .webm file, but we can peek inside using <code>ffmpeg</code>.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>ffmpeg<span class="w"> </span>-i<span class="w"> </span>SUPER<span class="se">\ </span>EUROBEAT<span class="se">\ </span>MIX-8B4guKLlbVU.webm<span class="w"> </span>
ffmpeg<span class="w"> </span>version<span class="w"> </span><span class="m">3</span>.4.2-1+b1<span class="w"> </span>Copyright<span class="w"> </span><span class="o">(</span>c<span class="o">)</span><span class="w"> </span><span class="m">2000</span>-2018<span class="w"> </span>the<span class="w"> </span>FFmpeg<span class="w"> </span>developers
<span class="w"> </span>built<span class="w"> </span>with<span class="w"> </span>gcc<span class="w"> </span><span class="m">7</span><span class="w"> </span><span class="o">(</span>Debian<span class="w"> </span><span class="m">7</span>.3.0-4<span class="o">)</span>
<span class="w"> </span>configuration:<span class="w"> </span>--prefix<span class="o">=</span>/usr<span class="w"> </span>--extra-version<span class="o">=</span><span class="m">1</span>+b1<span class="w"> </span>--toolchain<span class="o">=</span>hardened
<span class="w"> </span>--libdir<span class="o">=</span>/usr/lib/x86_64-linux-gnu<span class="w"> </span>--incdir<span class="o">=</span>/usr/include/x86_64-linux-gnu
<span class="w"> </span>--enable-gpl<span class="w"> </span>--disable-stripping<span class="w"> </span>--enable-avresample<span class="w"> </span>--enable-avisynth
<span class="w"> </span>--enable-gnutls<span class="w"> </span>--enable-ladspa<span class="w"> </span>--enable-libass<span class="w"> </span>--enable-libbluray
<span class="w"> </span>--enable-libbs2b<span class="w"> </span>--enable-libcaca<span class="w"> </span>--enable-libcdio<span class="w"> </span>--enable-libflite
<span class="w"> </span>--enable-libfontconfig<span class="w"> </span>--enable-libfreetype<span class="w"> </span>--enable-libfribidi<span class="w"> </span>--enable-libgme
<span class="w"> </span>--enable-libgsm<span class="w"> </span>--enable-libmp3lame<span class="w"> </span>--enable-libmysofa<span class="w"> </span>--enable-libopenjpeg
<span class="w"> </span>--enable-libopenmpt<span class="w"> </span>--enable-libopus<span class="w"> </span>--enable-libpulse<span class="w"> </span>--enable-librubberband
<span class="w"> </span>--enable-librsvg<span class="w"> </span>--enable-libshine<span class="w"> </span>--enable-libsnappy<span class="w"> </span>--enable-libsoxr
<span class="w"> </span>--enable-libspeex<span class="w"> </span>--enable-libssh<span class="w"> </span>--enable-libtheora<span class="w"> </span>--enable-libtwolame
<span class="w"> </span>--enable-libvorbis<span class="w"> </span>--enable-libvpx<span class="w"> </span>--enable-libwavpack<span class="w"> </span>--enable-libwebp
<span class="w"> </span>--enable-libx265<span class="w"> </span>--enable-libxml2<span class="w"> </span>--enable-libxvid<span class="w"> </span>--enable-libzmq
<span class="w"> </span>--enable-libzvbi<span class="w"> </span>--enable-omx<span class="w"> </span>--enable-openal<span class="w"> </span>--enable-opengl<span class="w"> </span>--enable-sdl2
<span class="w"> </span>--enable-libdc1394<span class="w"> </span>--enable-libdrm<span class="w"> </span>--enable-libiec61883<span class="w"> </span>--enable-chromaprint
<span class="w"> </span>--enable-frei0r<span class="w"> </span>--enable-libopencv<span class="w"> </span>--enable-libx264<span class="w"> </span>--enable-shared
<span class="w"> </span>libavutil<span class="w"> </span><span class="m">55</span>.<span class="w"> </span><span class="m">78</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">55</span>.<span class="w"> </span><span class="m">78</span>.100
<span class="w"> </span>libavcodec<span class="w"> </span><span class="m">57</span>.107.100<span class="w"> </span>/<span class="w"> </span><span class="m">57</span>.107.100
<span class="w"> </span>libavformat<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">83</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">83</span>.100
<span class="w"> </span>libavdevice<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">10</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">10</span>.100
<span class="w"> </span>libavfilter<span class="w"> </span><span class="m">6</span>.107.100<span class="w"> </span>/<span class="w"> </span><span class="m">6</span>.107.100
<span class="w"> </span>libavresample<span class="w"> </span><span class="m">3</span>.<span class="w"> </span><span class="m">7</span>.<span class="w"> </span><span class="m">0</span><span class="w"> </span>/<span class="w"> </span><span class="m">3</span>.<span class="w"> </span><span class="m">7</span>.<span class="w"> </span><span class="m">0</span>
<span class="w"> </span>libswscale<span class="w"> </span><span class="m">4</span>.<span class="w"> </span><span class="m">8</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">4</span>.<span class="w"> </span><span class="m">8</span>.100
<span class="w"> </span>libswresample<span class="w"> </span><span class="m">2</span>.<span class="w"> </span><span class="m">9</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">2</span>.<span class="w"> </span><span class="m">9</span>.100
<span class="w"> </span>libpostproc<span class="w"> </span><span class="m">54</span>.<span class="w"> </span><span class="m">7</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">54</span>.<span class="w"> </span><span class="m">7</span>.100
Input<span class="w"> </span><span class="c1">#0, matroska,webm, from 'SUPER EUROBEAT MIX-8B4guKLlbVU.webm':</span>
<span class="w"> </span>Metadata:
<span class="w"> </span>ENCODER<span class="w"> </span>:<span class="w"> </span>Lavf57.83.100
<span class="w"> </span>Duration:<span class="w"> </span><span class="m">01</span>:06:35.57,<span class="w"> </span>start:<span class="w"> </span><span class="m">0</span>.000000,<span class="w"> </span>bitrate:<span class="w"> </span><span class="m">490</span><span class="w"> </span>kb/s
<span class="w"> </span>Stream<span class="w"> </span><span class="c1">#0:0(eng): Video: vp9 (Profile 0), yuv420p(tv,</span>
bt709/unknown/unknown<span class="o">)</span>,<span class="w"> </span>1920x1080,<span class="w"> </span>SAR<span class="w"> </span><span class="m">1</span>:1<span class="w"> </span>DAR<span class="w"> </span><span class="m">16</span>:9,<span class="w"> </span><span class="m">29</span>.97<span class="w"> </span>fps,<span class="w"> </span><span class="m">29</span>.97<span class="w"> </span>tbr,<span class="w"> </span>1k
tbn,<span class="w"> </span>1k<span class="w"> </span>tbc<span class="w"> </span><span class="o">(</span>default<span class="o">)</span>
<span class="w"> </span>Metadata:
<span class="w"> </span>DURATION<span class="w"> </span>:<span class="w"> </span><span class="m">01</span>:06:35.558000000
<span class="w"> </span>Stream<span class="w"> </span><span class="c1">#0:1(eng): Audio: vorbis, 44100 Hz, stereo, fltp (default)</span>
<span class="w"> </span>Metadata:
<span class="w"> </span>DURATION<span class="w"> </span>:<span class="w"> </span><span class="m">01</span>:06:35.572000000
At<span class="w"> </span>least<span class="w"> </span>one<span class="w"> </span>output<span class="w"> </span>file<span class="w"> </span>must<span class="w"> </span>be<span class="w"> </span>specified
</code></pre></div>
<p>The important line is here:</p>
<div class="highlight"><pre><span></span><code>Stream #0:1(eng): Audio: vorbis, 44100 Hz, stereo, fltp (default)
</code></pre></div>
<p>The audio uses the "vorbis" codec. Hence, we should probably use the .ogg
extension for our output file, to ensure we specify a compatible audio
container format. If it were mp3-encoded, we'd use .mp3, and so on.</p>
<p>Let's extract the audio track from our video file. We need a couple new flags
for <code>ffmpeg</code>. The first is <code>-vn</code>, which tells <code>ffmpeg</code> to not include a video
track. The second is <code>-acodec copy</code>, which says we want to copy the existing
audio track, rather than re-encode it.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>ffmpeg<span class="w"> </span>-i<span class="w"> </span>SUPER<span class="se">\ </span>EUROBEAT<span class="se">\ </span>MIX-8B4guKLlbVU.webm<span class="w"> </span>-vn<span class="w"> </span>-acodec<span class="w"> </span>copy<span class="w"> </span>super_eurobeat_mix.ogg
ffmpeg<span class="w"> </span>version<span class="w"> </span><span class="m">3</span>.4.2-1+b1<span class="w"> </span>Copyright<span class="w"> </span><span class="o">(</span>c<span class="o">)</span><span class="w"> </span><span class="m">2000</span>-2018<span class="w"> </span>the<span class="w"> </span>FFmpeg<span class="w"> </span>developers
<span class="w"> </span>built<span class="w"> </span>with<span class="w"> </span>gcc<span class="w"> </span><span class="m">7</span><span class="w"> </span><span class="o">(</span>Debian<span class="w"> </span><span class="m">7</span>.3.0-4<span class="o">)</span>
<span class="w"> </span>configuration:<span class="w"> </span>--prefix<span class="o">=</span>/usr<span class="w"> </span>--extra-version<span class="o">=</span><span class="m">1</span>+b1<span class="w"> </span>--toolchain<span class="o">=</span>hardened
<span class="w"> </span>--libdir<span class="o">=</span>/usr/lib/x86_64-linux-gnu<span class="w"> </span>--incdir<span class="o">=</span>/usr/include/x86_64-linux-gnu
<span class="w"> </span>--enable-gpl<span class="w"> </span>--disable-stripping<span class="w"> </span>--enable-avresample<span class="w"> </span>--enable-avisynth
<span class="w"> </span>--enable-gnutls<span class="w"> </span>--enable-ladspa<span class="w"> </span>--enable-libass<span class="w"> </span>--enable-libbluray
<span class="w"> </span>--enable-libbs2b<span class="w"> </span>--enable-libcaca<span class="w"> </span>--enable-libcdio<span class="w"> </span>--enable-libflite
<span class="w"> </span>--enable-libfontconfig<span class="w"> </span>--enable-libfreetype<span class="w"> </span>--enable-libfribidi<span class="w"> </span>--enable-libgme
<span class="w"> </span>--enable-libgsm<span class="w"> </span>--enable-libmp3lame<span class="w"> </span>--enable-libmysofa<span class="w"> </span>--enable-libopenjpeg
<span class="w"> </span>--enable-libopenmpt<span class="w"> </span>--enable-libopus<span class="w"> </span>--enable-libpulse<span class="w"> </span>--enable-librubberband
<span class="w"> </span>--enable-librsvg<span class="w"> </span>--enable-libshine<span class="w"> </span>--enable-libsnappy<span class="w"> </span>--enable-libsoxr
<span class="w"> </span>--enable-libspeex<span class="w"> </span>--enable-libssh<span class="w"> </span>--enable-libtheora<span class="w"> </span>--enable-libtwolame
<span class="w"> </span>--enable-libvorbis<span class="w"> </span>--enable-libvpx<span class="w"> </span>--enable-libwavpack<span class="w"> </span>--enable-libwebp
<span class="w"> </span>--enable-libx265<span class="w"> </span>--enable-libxml2<span class="w"> </span>--enable-libxvid<span class="w"> </span>--enable-libzmq
<span class="w"> </span>--enable-libzvbi<span class="w"> </span>--enable-omx<span class="w"> </span>--enable-openal<span class="w"> </span>--enable-opengl<span class="w"> </span>--enable-sdl2
<span class="w"> </span>--enable-libdc1394<span class="w"> </span>--enable-libdrm<span class="w"> </span>--enable-libiec61883<span class="w"> </span>--enable-chromaprint
<span class="w"> </span>--enable-frei0r<span class="w"> </span>--enable-libopencv<span class="w"> </span>--enable-libx264<span class="w"> </span>--enable-shared
<span class="w"> </span>libavutil<span class="w"> </span><span class="m">55</span>.<span class="w"> </span><span class="m">78</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">55</span>.<span class="w"> </span><span class="m">78</span>.100
<span class="w"> </span>libavcodec<span class="w"> </span><span class="m">57</span>.107.100<span class="w"> </span>/<span class="w"> </span><span class="m">57</span>.107.100
<span class="w"> </span>libavformat<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">83</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">83</span>.100
<span class="w"> </span>libavdevice<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">10</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">57</span>.<span class="w"> </span><span class="m">10</span>.100
<span class="w"> </span>libavfilter<span class="w"> </span><span class="m">6</span>.107.100<span class="w"> </span>/<span class="w"> </span><span class="m">6</span>.107.100
<span class="w"> </span>libavresample<span class="w"> </span><span class="m">3</span>.<span class="w"> </span><span class="m">7</span>.<span class="w"> </span><span class="m">0</span><span class="w"> </span>/<span class="w"> </span><span class="m">3</span>.<span class="w"> </span><span class="m">7</span>.<span class="w"> </span><span class="m">0</span>
<span class="w"> </span>libswscale<span class="w"> </span><span class="m">4</span>.<span class="w"> </span><span class="m">8</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">4</span>.<span class="w"> </span><span class="m">8</span>.100
<span class="w"> </span>libswresample<span class="w"> </span><span class="m">2</span>.<span class="w"> </span><span class="m">9</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">2</span>.<span class="w"> </span><span class="m">9</span>.100
<span class="w"> </span>libpostproc<span class="w"> </span><span class="m">54</span>.<span class="w"> </span><span class="m">7</span>.100<span class="w"> </span>/<span class="w"> </span><span class="m">54</span>.<span class="w"> </span><span class="m">7</span>.100
Input<span class="w"> </span><span class="c1">#0, matroska,webm, from 'SUPER EUROBEAT MIX-8B4guKLlbVU.webm':</span>
<span class="w"> </span>Metadata:
<span class="w"> </span>ENCODER<span class="w"> </span>:<span class="w"> </span>Lavf57.83.100
<span class="w"> </span>Duration:<span class="w"> </span><span class="m">01</span>:06:35.57,<span class="w"> </span>start:<span class="w"> </span><span class="m">0</span>.000000,<span class="w"> </span>bitrate:<span class="w"> </span><span class="m">490</span><span class="w"> </span>kb/s
<span class="w"> </span>Stream<span class="w"> </span><span class="c1">#0:0(eng): Video: vp9 (Profile 0), yuv420p(tv,</span>
bt709/unknown/unknown<span class="o">)</span>,<span class="w"> </span>1920x1080,<span class="w"> </span>SAR<span class="w"> </span><span class="m">1</span>:1<span class="w"> </span>DAR<span class="w"> </span><span class="m">16</span>:9,<span class="w"> </span><span class="m">29</span>.97<span class="w"> </span>fps,<span class="w"> </span><span class="m">29</span>.97<span class="w"> </span>tbr,<span class="w"> </span>1k
tbn,<span class="w"> </span>1k<span class="w"> </span>tbc<span class="w"> </span><span class="o">(</span>default<span class="o">)</span>
<span class="w"> </span>Metadata:
<span class="w"> </span>DURATION<span class="w"> </span>:<span class="w"> </span><span class="m">01</span>:06:35.558000000
<span class="w"> </span>Stream<span class="w"> </span><span class="c1">#0:1(eng): Audio: vorbis, 44100 Hz, stereo, fltp (default)</span>
<span class="w"> </span>Metadata:
<span class="w"> </span>DURATION<span class="w"> </span>:<span class="w"> </span><span class="m">01</span>:06:35.572000000
Output<span class="w"> </span><span class="c1">#0, ogg, to 'super_eurobeat_mix.ogg':</span>
<span class="w"> </span>Metadata:
<span class="w"> </span>encoder<span class="w"> </span>:<span class="w"> </span>Lavf57.83.100
<span class="w"> </span>Stream<span class="w"> </span><span class="c1">#0:0(eng): Audio: vorbis, 44100 Hz, stereo, fltp (default)</span>
<span class="w"> </span>Metadata:
<span class="w"> </span>DURATION<span class="w"> </span>:<span class="w"> </span><span class="m">01</span>:06:35.572000000
<span class="w"> </span>encoder<span class="w"> </span>:<span class="w"> </span>Lavf57.83.100
Stream<span class="w"> </span>mapping:
<span class="w"> </span>Stream<span class="w"> </span><span class="c1">#0:1 -> #0:0 (copy)</span>
Press<span class="w"> </span><span class="o">[</span>q<span class="o">]</span><span class="w"> </span>to<span class="w"> </span>stop,<span class="w"> </span><span class="o">[</span>?<span class="o">]</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nb">help</span>
<span class="nv">size</span><span class="o">=</span><span class="w"> </span>60863kB<span class="w"> </span><span class="nv">time</span><span class="o">=</span><span class="m">01</span>:06:35.54<span class="w"> </span><span class="nv">bitrate</span><span class="o">=</span><span class="w"> </span><span class="m">124</span>.8kbits/s<span class="w"> </span><span class="nv">speed</span><span class="o">=</span><span class="m">1</span>.03e+03x<span class="w"> </span>
video:0kB<span class="w"> </span>audio:60330kB<span class="w"> </span>subtitle:0kB<span class="w"> </span>other<span class="w"> </span>streams:0kB<span class="w"> </span>global<span class="w"> </span>headers:4kB
muxing<span class="w"> </span>overhead:<span class="w"> </span><span class="m">0</span>.884396%
</code></pre></div>
<p>We've successfully extracted <code>super_eurobeat_mix.ogg</code> from our video file! Go
us!</p>
<p>Happy listening, and drive safely while you eurobeat. 🎵</p>Brooklyn.js' Fourth Birthday! November 20172017-11-16T00:00:00-05:002017-11-16T00:00:00-05:00Elana Hashmantag:hashman.ca,2017-11-16:/brooklyn-js-4.0/<p>I gave a talk for Brooklyn.js' fourth birthday about the state of the Debian Javascript ecosystem 🍥</p><p>This year, after being saddened about the state of the Javascript ecosystem in
Debian, I decided to propose a talk to Brooklyn.js to help raise awareness of
some of the problems and solutions, and encourage people to volunteer.</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true">
<p lang="en" dir="ltr">First,
<a href="https://twitter.com/ehashdn?ref_src=twsrc%5Etfw">@ehashdn</a>
is teaching us about the wonders of Debian & answering some questions
about the peculiarities of node & JavaScript in it 🐧🌀
<a href="https://t.co/M9KTmxW3aT">pic.twitter.com/M9KTmxW3aT</a>
</p>
— BrooklynJS (@brooklyn_js)
<a href="https://twitter.com/brooklyn_js/status/931329679264440320?ref_src=twsrc%5Etfw">November 17, 2017</a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2>"Why can't we just use /usr/bin/node?!" An introduction to Javascript in Debian</h2>
<ul>
<li><a href="https://github.com/brooklynjs/brooklynjs.github.io/issues/442">Talk description, Brooklyn.js GitHub repository</a></li>
<li><a href="/brooklyn-js-4.0/brooklyn_js_debian.pdf">Talk slides (pdf download)</a></li>
<li><a href="https://www.debian.org">About Debian</a></li>
<li><a href="https://packages.debian.org/sid/nodejs">nodejs package tracker (unstable)</a></li>
<li><a href="https://packages.debian.org/sid/npm">npm package tracker (unstable)</a></li>
</ul>
<h3>Image licensing info</h3>
<p>"There's no NEW queue for talk slides." – lamby, Debian Project Leader</p>
<ul>
<li>Debian logo, copyright 1999 <a href="https://www.debian.org/logos/">Software in the Public Interest,
Inc.</a> used under the <a href="http://creativecommons.org/licenses/by-sa/3.0/">Attribution-ShareAlike 3.0 Unported
License</a>.</li>
<li>The image of Tux is attributed to <a href="mailto:lewing@isc.tamu.edu">Larry Ewing</a> and <a href="https://www.gimp.org/">The GIMP</a>.</li>
<li>Debian family tree, authors Andreas Lundqvist, Donjan Rodic, modified by
<a href="https://commons.wikimedia.org/wiki/User:Michaeldsuarez">Michaeldsuarez</a>, used under the <a href="https://www.gnu.org/licenses/fdl-1.3.en.html">GNU Free Documentation
License, v1.3</a>.</li>
</ul>Fun Facts About Email2017-10-23T00:00:00-04:002017-10-23T00:00:00-04:00Elana Hashmantag:hashman.ca,2017-10-23:/email-tweets/<p>About a month ago, I put together a "1 like = 1 bad fact about email" thread on
Twitter. This got pretty popular and I'm too …</p><p>About a month ago, I put together a "1 like = 1 bad fact about email" thread on
Twitter. This got pretty popular and I'm too lazy to turn it into a real blog
post, so here's a link to the thread:</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true">
<p lang="en" dir="ltr">
Okay let's do this
<br>
<br>
1 like = 1 bad fact about email
</p>
— e. hashman (@ehashdn)
<a href="https://twitter.com/ehashdn/status/910514754246017025?ref_src=twsrc%5Etfw">September 20, 2017</a>
</blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>AnsibleFest 2017 Talk Resources2017-09-07T00:00:00-04:002017-09-07T00:00:00-04:00Elana Hashmantag:hashman.ca,2017-09-07:/ansiblefest-2017/<p>Talk resources for "Infrastructure Testing with Molecule", presented at AnsibleFest 2017.</p><p>At <a href="https://www.ansible.com/infastructure-testing-with-molecule">AnsibleFest 2017</a>, I gave a talk called "Infrastructure Testing with
Molecule". I also presented this talk at our annual Rackspace tech conference,
<a href="https://raxio2017.sched.com/event/CFyk/ansible-infrastructure-testing-with-molecule">RAX.IO 2017</a>. Here's some links and resources related to my talk, for
your reference.</p>
<blockquote class="twitter-tweet tw-align-center" data-lang="en" data-dnt="true">
<p lang="en" dir="ltr">
.<a href="https://twitter.com/ehashdn?ref_src=twsrc%5Etfw">@ehashdn</a> just
pulled off a live demo of Molecule complete w/ writing expected cowsay output
and got it right on the first try.
<a href="https://twitter.com/hashtag/AnsibleFest?src=hash&ref_src=twsrc%5Etfw">#AnsibleFest</a>
<a href="https://t.co/2GFftwei6Y">pic.twitter.com/2GFftwei6Y</a>
</p>
— Lisa Danz (@LisaDanz)
<a href="https://twitter.com/LisaDanz/status/905912781395730432?ref_src=twsrc%5Etfw">September
, 2017</a>
</blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2>Infrastructure Testing with Molecule</h2>
<ul>
<li><a href="https://www.ansible.com/infastructure-testing-with-molecule">Talk video</a></li>
<li><a href="/ansiblefest-2017/ansiblefest-2017-ehashman.pdf">Talk slides (pdf download)</a></li>
<li><a href="https://github.com/ehashman/ansiblefest">Live demo code</a></li>
<li><a href="http://molecule.readthedocs.io/en/stable-1.25/index.html">Official Molecule documentation</a>, version 1.25.0</li>
<li><a href="http://docs.ansible.com/ansible/latest/test_strategies.html">Official Ansible documentation on testing</a></li>
<li><a href="https://github.com/nylas/ansible-test">ansible-test</a> by Nylas</li>
<li><a href="http://kitchen.ci">Test Kitchen documentation</a></li>
<li><a href="https://hodgkins.io/testing-ansible-roles-windows-test-kitchen">Testing Windows Ansible Roles with Test Kitchen</a>, by Matthew Hodgkins</li>
</ul>Protect Yo'Self! Jan. 2017 Encryption Workshop Resources2017-01-28T00:00:00-05:002017-01-28T00:00:00-05:00Elana Hashmantag:hashman.ca,2017-01-28:/protect-yoself/<p>I ran an intro-to-encryption workshop at CodeNewbie's cryptoparty 🔐</p><p>Saron Yitbarek of <a href="http://www.codenewbie.org/">CodeNewbie</a> hosted a friendly
cryptoparty for beginners called "Protect Yo'Self! A day of workshops on
security and online privacy" that I was thrilled to be able to lead a workshop
at. I wrote a lecture, activities, and curriculum for a section on encryption,
which I led four times in a row from noon to 4PM. Phew!</p>
<div style="text-align: center">
<img alt="A photo of Elana Hashman leading this encryption workshop"
src="/protect-yoself/crypto_lecture.jpg" />
<br />
<strong>Candid shot of my lecture, with messaging and network diagrams</strong>
</div>
<h2>An Encryption Workshop for Beginners</h2>
<ul>
<li><a href="https://www.eventbrite.com/e/protect-yoself-a-day-of-workshops-on-security-and-online-privacy-tickets-31114213450#">EventBrite link</a></li>
<li><a href="https://hashman.ca/encryption-workshop/">Workshop Resources Page</a></li>
</ul>
<p>If there's enough popular demand, I may work to turn my lecture notes into a
series of blog posts.</p>Brooklyn.js' Third Birthday! November 20162016-11-17T00:00:00-05:002016-11-17T00:00:00-05:00Elana Hashmantag:hashman.ca,2016-11-17:/brooklyn-js-3.0/<p>I gave a talk for Brooklyn.js' third birthday about ham (amateur) radio 📻</p><p>Brian of Brooklyn.js had been bugging me to submit this talk ever since I
gave a rather weak pitch for it in May? of 2016. Eventually, I got around to
<a href="https://github.com/brooklynjs/brooklynjs.github.io/pull/339">writing up a proposal</a> and was accepted for the momentous third
birthday of Brooklyn.js! No one warned me that Brendan Eich would be in
attendance...</p>
<div style="text-align: center">
<img alt="Elana Hashman pointing at a K7YAM QSL card, which features a yam"
src="/brooklyn-js-3.0/qsl.jpg" />
<br />
<strong>Audience member's photo of a fun QSL card slide</strong>
</div>
<h2>AMATEUR RADIO: or, the original world wide indie web</h2>
<p>Eventually, I will upload the slides and my notes (since the slides are all
pictures and mostly meaningless without context). For now, enjoy the yam.</p>UWaterloo Steam Tunnels2016-10-29T00:00:00-04:002016-10-29T00:00:00-04:00Elana Hashmantag:hashman.ca,2016-10-29:/tunnels/<p>A series of photos from 2014 of the University of Waterloo steam tunnels.</p><div style="text-align: center">
<img alt="Black and white image down a steam tunnel. A sign says 'WATCH YOUR HEAD - DANGER'."
src="/images/tunnels/tunnels_00.jpg" />
<br />
</div>
<p>A couple years ago I took my camera down with me for a tour of the UWaterloo
steam tunnels.</p>
<p>The only photos I've ever been able to find online are <a href="http://matt.wandel.ca/tunnels/tunnels.html">Matt Wandel's,</a>
but they long predate DSLR technology. While things haven't changed much since
the 1990s, I wondered if people wouldn't appreciate a fresh perspective.</p>
<p>I am both an amateur photographer and a weird free software zealot who doesn't
understand how to use a Mac or proprietary software in general, so these were
all processed using software called <a href="http://www.darktable.org/">darktable</a>, an Adobe Lightroom
clone.</p>
<p>I assert my copyright on all these photos, but I'm pleased to distribute them
in unwatermarked format for the viewer's enjoyment. If you'd like to request
copies of the higher resolution originals for some reason, please get in touch.</p>Open Source Bridge 2016 Talk Resources2016-06-21T00:00:00-04:002016-06-21T00:00:00-04:00Elana Hashmantag:hashman.ca,2016-06-21:/osb-2016/<p>Talk resources for "Bringing OOP Best Practices to the World of Functional Programming", presented at Open Source Bridge 2016.</p><p>At Open Source Bridge 2016, I gave a talk called <a href="http://opensourcebridge.org/sessions/1817">Bringing OOP Best Practices
to the World of Functional Programming</a>. Then I gave a modified
version of this talk for the <a href="http://csclub.uwaterloo.ca/events/MC_4021-2016-10-06-6:00_pm">University of Waterloo Computer Science
Club</a> and for the <a href="https://www.meetup.com/Women-Who-Code-DC/events/242932833/">Lambda Ladies DC chapter</a>. Here's
some links and resources related to my talk, for your reference.</p>
<div style="text-align: center">
<img alt="A photo of Elana Hashman presenting at Open Source Bridge 2016"
src="/osb-2016/osb_talk.jpg" />
<br />
<strong>Candid shot of my presentation at Open Source Bridge 2016</strong>
</div>
<h2>Bringing OOP Best Practices to the World of Functional Programming</h2>
<ul>
<li><a href="http://opensourcebridge.org/sessions/1817">Talk description, Open Source Bridge 2016 website</a></li>
<li><a href="/osb-2016/osb_2016_ehashman.pdf">Talk slides (pdf download)</a></li>
<li><a href="/osb-2016/csc_2016_ehashman.pdf">Talk slides, CSC edition (pdf download)</a></li>
<li><a href="/osb-2016/lldc_2017_ehashman.pdf">Talk slides, Lambda Ladies edition (pdf download)</a></li>
<li><a href="https://en.wikipedia.org/wiki/Design_Patterns"><em>Design Patterns</em> on Wikipedia</a></li>
<li><a href="http://www.norvig.com/design-patterns/">"Design Patterns in Dynamic Languages" by Peter Norvig</a></li>
</ul>
<p>"Meta" UML diagram, Adapter Pattern UML Diagram, Strategy Pattern UML
diagram are all public domain images.</p>
<p>The following images were used under the <a href="http://creativecommons.org/licenses/by-sa/3.0/">Attribution-ShareAlike 3.0 Unported
License</a>:</p>
<ul>
<li>UML diagram of composition over inheritance by <a href="https://commons.wikimedia.org/wiki/File:UML_diagram_of_composition_over_inheritance.svg">Sae1962</a></li>
<li>Template Method: UML Class Diagram by <a href="https://commons.wikimedia.org/wiki/File:Template_Method_UML.svg">Giacomo Ritucci</a></li>
<li>Facade Design Pattern in UML by <a href="https://en.wikipedia.org/wiki/File:Example_of_Facade_design_pattern_in_UML.png">Fuhrmanator</a></li>
</ul>PyCon 2016 Talk Resources2016-05-29T00:00:00-04:002016-05-29T00:00:00-04:00Elana Hashmantag:hashman.ca,2016-05-29:/pycon-2016/<p>Talk resources for "Teaching Python: The Hard Parts", presented at PyCon US 2016.</p><p>At PyCon 2016, I gave a talk called <a
href="https://us.pycon.org/2016/schedule/presentation/2012/">Teaching Python:
The Hard Parts</a>. Here's some links and resources related to my talk, for
your reference.</p>
<h2>Teaching Python: The Hard Parts</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=CjYEpVNbM-s">Talk recording, hosted on YouTube</a></li>
<li><a href="https://us.pycon.org/2016/schedule/presentation/2012/">Talk description, PyCon 2016 website</a></li>
<li><a href="/pycon-2016/pycon_2016_ehashman.pdf">Talk slides (pdf download)</a></li>
<li><a href="http://wics.uwaterloo.ca/2015/09/pwfb-followup/">Follow-up PWFB blog post, WiCS UW website</a></li>
<li><a href="http://wiki.communitydata.cc/Community_Data_Science_Workshops">Community Data Science Workshops (CDSW), wiki</a></li>
<li><a href="http://wiki.openhatch.org/Python_Workshops_for_Beginners/Reflections">Reflections doc, PWFB</a></li>
<li><a href="http://wiki.communitydata.cc/index.php?search=reflections">Reflections docs, CDSW</a></li>
</ul>
<iframe style="display: block; margin: auto;" width="560" height="315" src="https://www.youtube-nocookie.com/embed/CjYEpVNbM-s" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<p>CDSW Photo in talk slides is by <a
href="http://wiki.communitydata.cc/File:Cdsw_combo_images-1.jpg">Benjamin Mako
Hill</a> and is used under the <a
href="http://creativecommons.org/licenses/by-sa/3.0/">Attribution-Share Alike
3.0 Unported License</a>.</p>
<p>"Questions?" kitten photo in talk slides is by <a
href="https://www.flickr.com/photos/lachlanrogers/">latch.r</a> and is used
under the <a
href="http://creativecommons.org/licenses/by-sa/2.0/">Attribution-ShareAlike
2.0 Generic License</a>.</p>What is a speech without an audience?2015-06-12T00:00:00-04:002015-06-12T00:00:00-04:00Elana Hashmantag:hashman.ca,2015-06-12:/speech/<p>Having now officially convocated, I feel comfortable posting what I wrote for
my valedictory nomination. I was not selected for the honour, but sometimes I …</p><p>Having now officially convocated, I feel comfortable posting what I wrote for
my valedictory nomination. I was not selected for the honour, but sometimes I
like to indulge in a bit of ahistorical fantasy. What would today have been
like if I had delivered some polished version of this piece?</p>
<h2>Valedictory Address, Mathematics Class of 2015, First Draft</h2>
<p>Mr. Chancellor, members of convocation, fellow graduates, and ladies and
gentlemen... I must say I am ecstatic to be here, as this means I have seen my
last contour integral, if I have any say in the matter.</p>
<p>I have the honour to stand before you today and reflect on your future. It is
so energizing to welcome the threshold of your new and exciting life.</p>
<p>The University of Waterloo, is, of course, famous for "the spirit of 'Why
not?'" We are the most innovative university in Canada, for God knows how many
years in a row now. I suppose it wouldn't be particularly welcome to innovate
on that trend, so I expect you graduates to keep up the good work. Now, don't
worry; if you didn't live at Velocity for the past five years, I intend to give
you a primer right now to ensure the long life of our fine reputation here at
the University of Waterloo.</p>
<p>Let's begin with a brief discussion of the properties of the Riemann zeta
function, despite your voiced objections—now, I hope you're all familiar
with this beautiful enigma, and it does spark some interesting conversations.
For instance, earlier in the term I was speaking with a post-doctorate student
who was telling me that his calculus students were thoroughly convinced that
the sum of all the positive integers is -1/12! Now, I'm not sure what branch of
analysis you subscribe to, but surely you must agree that the sum of all
positive integers diverges. I can see some of your heads spinning already...
no matter, the flashback to first year calculus will end shortly. So this
postdoc was telling me his students had told him that their physics professor
had taught them this result, and if you're not familiar, an analytic
continuation of the zeta function at the point -1 gives us this curious answer
of -1/12. Beautiful result from complex analysis. This is useful in making
string theory and quantum electrodynamics work, among other things.</p>
<p>So you tell me: which is it? Infinity or -1/12? <strong>[pause]</strong> I expected a good
representation of the two answers: both are correct. We know that the zeta
function evaluated at -1 gives us -1/12, but of course, we also know that the
implication—that the sum of all positive integers converges, let alone to
a negative number—is absurd. One of them must be wrong! Yet there is deep
theory that we can use consistently to justify either answer. I can imagine
those frosh thinking, "I didn't sign up for this!" Maybe you recall a similar
experience.</p>
<p>One thing I've noticed in the time I've spent here is that many of us are
attracted to mathematics for its elegance, its logic, its consistency.
Sometimes—and perhaps I romanticize a bit—but sometimes I feel as
though in mathematics lies the key to truth. And, then Dr. Kurt Gödel comes
along, in strict postmodernist style, and softly explains that it is not
possible for our mathematics to be consistent and complete. It seems that the
further we advance in our studies of the field, the less certain we can be
about any truth. We say that Choice is obvious, but then Banach-Tarski devours
my enthusiasm. Sometimes it feels as though my refuge is a sanatorium.</p>
<p>You've spent four or five years now collecting knowledge, learning to think
critically, perfecting your procrastination techniques, cooking on a shoestring
budget, and kicking yourself over and over again for that STAT midterm. For the
most part, change in our lives has been gradual, but every so often—and
this is something I just adore about mathematics—there's this burst of
inspiration, an epiphany, and the answer just clicks into place. Or sometimes,
you see things so clearly that everything unravels and you realize that you
can't tie the loose threads back together. Change is frightening.</p>
<p>These are moments in which danger lies: we may come tantalizingly close to the
threshold of understanding, but then, out of fear and uncertainty, we back
away. We lure ourselves into false comfort that mathematics is logical and
complete and it holds all the answers: and then it strikes us back with
emptiness and paradox. But this is not confined to math: when I see students on
this campus faced with issues like discrimination and the challenges of mental
health, too often do I see my peers justify injustice by arguing it must have
been deserved. Too often do I watch "The Meritocracy" of STEM held up as
tautological evidence of its own superiority, while simultaneously watching
these pundits tear down another classmate's circular proof. How do you explain
this contradiction?</p>
<p>My fellow graduates, I promised you a primer on how to innovate, how to enact
change. And so I take this these few, precious minutes to focus on this simple,
uncomfortable concept. Because as we all know, a mathematics degree alone is
neither necessary nor sufficient for success, but we stand here together to
tell the world that it matters, that we can think, that we can solve problems.
I know you can solve problems. So let us tackle the hard ones. Let us embrace
the cognitive dissonance.</p>
<p>For the last five years of my life, I have experienced contradiction after
contradiction that struck me with discomfort, tore my eyes open, spurned me to
change, spurned me to action. They tell me that Waterloo students are some of
the most apathetic in the country, but when I watch you persist in your studies
I know this cannot be further from the truth. You have the fundamentals
mastered, but I suggest you consider cross-training. I challenge you: let us
embrace the contradiction inherent in the pursuit of any complete and
consistent system. The world is waiting for you, and someone is going to ask
you to choose between infinity and -1/12. And I hope you pick one, and I hope
you justify it to the fullest of your ability, and I hope that you hold in your
heart that you are still wrong, and embrace it.</p>
<p>Thank you.</p>