<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eddie Gulay</title>
    <description>The latest articles on DEV Community by Eddie Gulay (@eddiegulay).</description>
    <link>https://dev.to/eddiegulay</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1162732%2Fa728d062-8f1d-4b0f-a055-2672e229f637.png</url>
      <title>DEV Community: Eddie Gulay</title>
      <link>https://dev.to/eddiegulay</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eddiegulay"/>
    <language>en</language>
    <item>
      <title>I built a launcher I can't argue with</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Thu, 18 Jun 2026 08:05:26 +0000</pubDate>
      <link>https://dev.to/eddiegulay/i-built-a-launcher-i-cant-argue-with-3nl2</link>
      <guid>https://dev.to/eddiegulay/i-built-a-launcher-i-cant-argue-with-3nl2</guid>
      <description>&lt;p&gt;I doomscroll. A lot. The honest version: I'd unlock my phone to check the time and resurface forty minutes later, having read nothing I'll remember. I tried every "minimalist launcher" out there, the kind that hides apps behind a text list. They work for about a week, until your thumb learns the new muscle memory and you're back. Hiding isn't blocking. "Hidden until I cave" is just a speed bump.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Tempo&lt;/strong&gt;: a home-screen replacement that does three quiet things, tell the time, find an app, show notifications, on a calm washi-paper canvas. And it has one feature none of the others do. When you hide an app, it's gone for &lt;strong&gt;10 days, with no take-backs, and the block survives uninstalling the app.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the build log: what it feels like, why the blockade is the whole point, and the engineering that makes it stick. It's MIT-licensed and the repo is linked at the bottom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcc6h3evfubctf810fzyj.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcc6h3evfubctf810fzyj.jpeg" alt="Tempo home and search" width="485" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it feels like
&lt;/h2&gt;

&lt;p&gt;There are exactly three screens, reached from a floating dock pill:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Home (ホーム).&lt;/strong&gt; A faint sumi-e ensō ring behind a large mincho clock, the date in vertical Reiwa-era kanji (令和八年・六月十八日), a spoken-style reading, and a single vermillion 静 ("stillness") seal. That's it. No icons, no widgets, no folders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search (検索).&lt;/strong&gt; A live-filtered list of every installed app. No grid to graze on; you have to know what you came for and type it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications (通知).&lt;/strong&gt; Your real notifications, tap to open, swipe to dismiss.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole thing is in Japanese, a language I can't actually read. That's deliberate. The friction is the feature: a phone that's slightly harder to use mindlessly is a phone you use less. The aesthetic isn't decoration, it's a speed bump you chose on purpose.&lt;/p&gt;

&lt;p&gt;There's a light "Paper" theme (washi cream) and a dark "Sumi" theme (warm charcoal, not the usual true-black void). Both run a faint paper-grain pass so it reads like a sheet, not a screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one idea that matters: a block you can't take back
&lt;/h2&gt;

&lt;p&gt;Every other minimalist launcher treats hiding as a toggle. Tempo treats it as a commitment device. Hide an app and you confirm a 10-day sentence. While it's blocked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it's gone from Search,&lt;/li&gt;
&lt;li&gt;its notifications are suppressed system-wide (via a &lt;code&gt;NotificationListenerService&lt;/code&gt;), and&lt;/li&gt;
&lt;li&gt;it cannot be un-hidden until the 10 days elapse. Not by tapping, not by clearing data, &lt;strong&gt;not by uninstalling Tempo and reinstalling it.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last clause is the hard part, and it's where most "strict mode" features quietly cheat. If your block lives in app storage, it dies the moment the user reinstalls, a 15-second escape hatch. A real commitment device has to close that door.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the blockade actually sticks
&lt;/h2&gt;

&lt;p&gt;The ledger is dead simple: a map of &lt;code&gt;package -&amp;gt; unlockAt&lt;/code&gt; (absolute epoch-millis). Presence means hidden; un-hiding is refused until &lt;code&gt;now()&lt;/code&gt; passes &lt;code&gt;unlockAt&lt;/code&gt;. The interesting part is defending it against the two obvious cheats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cheat 1: uninstall and reinstall to wipe the ledger.&lt;/strong&gt; App-private storage is erased on uninstall, so the ledger is also mirrored to a dotfile in shared storage (&lt;code&gt;Documents/.tempo_keep.json&lt;/code&gt;) through All-files access. On a fresh install the two ledgers are reconciled, and per package the later &lt;code&gt;unlockAt&lt;/code&gt; always wins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;mergeByMax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?):&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;out&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nf"&gt;emptyMap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unlockAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;maxOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="mi"&gt;0L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unlockAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reinstalling can never shorten a block; at worst the merge is a no-op. The same rule applies when you start a new block: it &lt;code&gt;maxOf&lt;/code&gt;s against any existing one, so the system can extend but never reduce.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cheat 2: wind the system clock back.&lt;/strong&gt; If "now" is just &lt;code&gt;System.currentTimeMillis()&lt;/code&gt;, you set the date to next month and the block evaporates. So Tempo keeps a monotonic high-water mark and reads time as the max of the two:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** Guarded "now": never earlier than the highest time we've previously observed. */&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;maxOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;lastSeen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;lastSeen&lt;/code&gt; is persisted in the same ledger and bumped on every observation. Roll the clock back and &lt;code&gt;now()&lt;/code&gt; simply doesn't move; roll it forward and you've only sped up your own sentence.&lt;/p&gt;

&lt;p&gt;One honest caveat I put right in the source: this is best-effort, not tamper-proof. A determined user can still delete the shared dotfile or factory-reset. Truly unbypassable enforcement needs Device Owner provisioning, which is way out of scope for a launcher you install for yourself. The bar I'm aiming for isn't "defeats a motivated attacker," it's "defeats me at 11pm with low willpower," and for that, closing the reinstall and clock-rollback doors is enough.&lt;/p&gt;

&lt;p&gt;The whole thing is a single &lt;code&gt;BlockadeRepository&lt;/code&gt;: seed synchronously at startup so the first Search frame already excludes blocked apps (no flash of the thing you're trying not to see), then reconcile against the durable mirror off the main thread.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of the stack
&lt;/h2&gt;

&lt;p&gt;It's deliberately small. No DI framework, no third-party UI libraries, just AndroidX.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;100% Jetpack Compose&lt;/strong&gt;, drawn edge-to-edge. One &lt;code&gt;LauncherViewModel&lt;/code&gt; over three repositories (apps, theme, blockade). State in &lt;code&gt;DataStore&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;LauncherApps&lt;/code&gt;&lt;/strong&gt; for a live, profile-aware app inventory (work-profile apps included), kept current with its callback rather than a one-shot &lt;code&gt;PackageManager&lt;/code&gt; query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theming&lt;/strong&gt; is a tiny &lt;code&gt;TempoColors&lt;/code&gt; data class behind a &lt;code&gt;CompositionLocal&lt;/code&gt;, with the background painted as a top-anchored radial wash plus a cached noise tile, multiplied in Paper, screened in Sumi so the grain catches light instead of muddying the charcoal.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawing my own icons
&lt;/h3&gt;

&lt;p&gt;A drawer full of the OS's colorful, mismatched icons wrecks the calm. So Tempo doesn't use them. It ships an internal library of monochrome line glyphs, hand-written 24×24 SVG paths stroked with round caps via Compose's &lt;code&gt;PathParser&lt;/code&gt;, and resolves each installed app to one by function in four passes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;exact package id (the top ~140 apps, so &lt;code&gt;com.spotify.music&lt;/code&gt; maps to music),&lt;/li&gt;
&lt;li&gt;a keyword in the package id (&lt;code&gt;...dialer&lt;/code&gt; maps to phone),&lt;/li&gt;
&lt;li&gt;a keyword in the display name, English or Japanese (&lt;code&gt;天気&lt;/code&gt; / "weather" maps to a cloud),&lt;/li&gt;
&lt;li&gt;the declared &lt;code&gt;ApplicationInfo&lt;/code&gt; category.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Anything unmatched falls back to a washi monogram tile of its first character, so even the long tail looks intentional. The trade-off is obvious: a niche app won't get a bespoke glyph. But the drawer reads as one coherent sheet instead of a ransom note, and I deleted the entire icon-bitmap decode and cache path in the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting it to 4.5 MB
&lt;/h3&gt;

&lt;p&gt;The first release was 36 MB. Almost all of it was fonts: two full Shippori Mincho CJK weights at roughly 8.6 MB each, plus two Zen Kaku Gothic weights. A launcher does not need 20,000 kanji.&lt;/p&gt;

&lt;p&gt;I subset them with &lt;code&gt;pyftsubset&lt;/code&gt; to Latin, kana, the 2,136 jōyō kanji, and every glyph the UI literally uses, with the system Noto font as graceful fallback for anything rarer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyftsubset shippori_mincho_regular.ttf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;out.ttf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--unicodes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;U+0020-007E,U+3000-30FF,U+FF00-FFEF &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--text-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;joyo+ui-glyphs.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--layout-features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt; &lt;span class="nt"&gt;--no-hinting&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That alone took fonts from 21 MB to 3.9 MB. Turning on R8 (the release build had optimization disabled) collapsed the code, resource shrinking cleaned up the rest, and an ABI filter dropped the useless x86 stubs. Final signed APK: roughly 4.5 MB, an 88% cut, with no visible change to the design.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shipping it
&lt;/h3&gt;

&lt;p&gt;Releases are a git tag away. A GitHub Actions workflow triggers on &lt;code&gt;v*&lt;/code&gt;, decodes the signing keystore from a base64 secret, derives the version name from the tag, builds the signed APK, and publishes a Release with the artifact attached:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag v0.0.4 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git push origin v0.0.4
&lt;span class="c"&gt;# CI builds Tempo-v0.0.4.apk, signed, attached to the GitHub Release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The keystore never lives in the repo; signing config reads from &lt;code&gt;local.properties&lt;/code&gt; locally or env-injected secrets in CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd flag if you build something similar
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;All-files access is a heavy permission&lt;/strong&gt; to ask for, and rightly scrutinized. I only use it to write one dotfile, and the app degrades gracefully without it (the block still works, it just won't survive a reinstall). Be honest with users about why you need it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A user-hostile feature is a design tightrope.&lt;/strong&gt; "You literally cannot undo this for 10 days" is the point, but it means the confirmation copy has to be unambiguous and the countdown always visible. If it ever feels like a bug instead of a choice, you've lost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subsetting fonts is the highest-leverage size win&lt;/strong&gt; for any app bundling CJK or icon fonts, and almost nobody does it. Start there before micro-optimizing dex.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it, take it apart
&lt;/h2&gt;

&lt;p&gt;Tempo is open source (MIT), built with Kotlin and Jetpack Compose, minSdk 35. Grab the latest signed APK from the releases page, or clone it and read the &lt;code&gt;BlockadeRepository&lt;/code&gt;, the most interesting 160 lines in the project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/eddiegulay/tempo" rel="noopener noreferrer"&gt;https://github.com/eddiegulay/tempo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Releases (download the APK): &lt;a href="https://github.com/eddiegulay/tempo/releases" rel="noopener noreferrer"&gt;https://github.com/eddiegulay/tempo/releases&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've also tried and failed to out-discipline your own thumb, I'd genuinely like to hear whether a block you can't take back works for you the way it's working for me.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Under-presentation of Swahili in AI tasks</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Sat, 10 Jan 2026 19:32:14 +0000</pubDate>
      <link>https://dev.to/eddiegulay/underpresentation-of-swahili-in-ai-tasks-4d44</link>
      <guid>https://dev.to/eddiegulay/underpresentation-of-swahili-in-ai-tasks-4d44</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;the cover image is also underpresented!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Swahili is significantly underrepresented in AI research and applications, especially compared to languages like English, Mandarin, Spanish, or even French. A few key points highlight this gap:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Scarcity&lt;/strong&gt;: Large-scale datasets in Swahili are limited. Most NLP models rely on massive text corpora to learn patterns, but Swahili content online is comparatively small, fragmented, or noisy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limited Pretrained Models&lt;/strong&gt;: While there are some multilingual models like mBERT or XLM-R, they underperform on Swahili because the language is a small fraction of their training data. Truly high-performing, Swahili-specific models are rare.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Low Research Focus&lt;/strong&gt;: Academic and industry research in NLP and speech processing often overlooks Swahili. Few papers focus on tasks like sentiment analysis, machine translation, or speech recognition for Swahili.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Speech and Multimodal Gaps&lt;/strong&gt;: Swahili speech datasets, handwritten text, and multimodal datasets (images with Swahili captions, videos, etc.) are almost non-existent. This makes building voice assistants, OCR, or image captioning models in Swahili extremely challenging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Impact on Applications&lt;/strong&gt;: This underrepresentation affects practical AI applications—chatbots, translation services, digital assistants, and educational tools often fail to work well for Swahili speakers.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Aand.. Here's a detailed table of AI and ML tasks where Swahili is underrepresented, organized by category. I’ve included the &lt;strong&gt;task&lt;/strong&gt;, &lt;strong&gt;current state for Swahili&lt;/strong&gt;, and &lt;strong&gt;potential impact if addressed&lt;/strong&gt;. This should give a clear sense of both gaps and opportunities.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Category&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;AI Task&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Current State for Swahili&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Potential Impact if Developed&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Natural Language Processing (NLP)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Language Modeling&lt;/td&gt;
&lt;td&gt;Few large-scale Swahili corpora; multilingual models underperform&lt;/td&gt;
&lt;td&gt;Better text generation, predictive typing, writing aids&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Text Classification&lt;/td&gt;
&lt;td&gt;Very limited labeled datasets for topics, sentiment, or spam detection&lt;/td&gt;
&lt;td&gt;Improved moderation, content filtering, sentiment analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Sentiment Analysis&lt;/td&gt;
&lt;td&gt;Almost no high-quality annotated datasets&lt;/td&gt;
&lt;td&gt;Social media monitoring, brand analysis, public opinion insights&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Named Entity Recognition (NER)&lt;/td&gt;
&lt;td&gt;Few datasets; existing NER models often fail on Swahili text&lt;/td&gt;
&lt;td&gt;Improved information extraction for news, legal, and healthcare texts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Part-of-Speech Tagging&lt;/td&gt;
&lt;td&gt;Sparse corpora; rules-based systems dominate&lt;/td&gt;
&lt;td&gt;Better grammar analysis, parsing, and downstream NLP tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Machine Translation&lt;/td&gt;
&lt;td&gt;Limited parallel corpora; Google Translate quality varies&lt;/td&gt;
&lt;td&gt;Accurate translation for education, business, and government documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Summarization&lt;/td&gt;
&lt;td&gt;Almost nonexistent datasets or pretrained models&lt;/td&gt;
&lt;td&gt;Automated content summarization for news, legal, and academic texts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Question Answering&lt;/td&gt;
&lt;td&gt;Very few datasets; models trained on English fail on Swahili&lt;/td&gt;
&lt;td&gt;AI assistants, educational tools, customer support systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Semantic Search / Retrieval&lt;/td&gt;
&lt;td&gt;Limited indexing and embeddings in Swahili&lt;/td&gt;
&lt;td&gt;Efficient document retrieval, knowledge bases, and search engines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speech &amp;amp; Audio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatic Speech Recognition (ASR)&lt;/td&gt;
&lt;td&gt;Few large-scale Swahili audio datasets&lt;/td&gt;
&lt;td&gt;Voice assistants, dictation tools, transcription services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Text-to-Speech (TTS)&lt;/td&gt;
&lt;td&gt;Limited high-quality Swahili voice models&lt;/td&gt;
&lt;td&gt;Assistive tech, IVR systems, audiobooks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Speech Translation&lt;/td&gt;
&lt;td&gt;Almost nonexistent&lt;/td&gt;
&lt;td&gt;Real-time communication across languages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Speaker Diarization&lt;/td&gt;
&lt;td&gt;Rare for Swahili&lt;/td&gt;
&lt;td&gt;Meeting transcription, call center analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multimodal AI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Image Captioning&lt;/td&gt;
&lt;td&gt;No significant Swahili-labeled image datasets&lt;/td&gt;
&lt;td&gt;Accessibility tools, educational resources, social media tagging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;OCR (Optical Character Recognition)&lt;/td&gt;
&lt;td&gt;Some work on printed Swahili; handwritten datasets very rare&lt;/td&gt;
&lt;td&gt;Digitalizing documents, preserving literature and historical texts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Video Understanding&lt;/td&gt;
&lt;td&gt;No datasets with Swahili captions or narration&lt;/td&gt;
&lt;td&gt;Subtitling, content indexing, AI tutors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dialog &amp;amp; Conversational AI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chatbots&lt;/td&gt;
&lt;td&gt;Very few Swahili-trained models&lt;/td&gt;
&lt;td&gt;Customer support, education, e-government services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Dialogue Summarization&lt;/td&gt;
&lt;td&gt;Almost no datasets&lt;/td&gt;
&lt;td&gt;Meeting notes, conversational analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Intent Recognition&lt;/td&gt;
&lt;td&gt;Few datasets&lt;/td&gt;
&lt;td&gt;Better automation for local businesses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recommendation Systems&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Content Recommendation&lt;/td&gt;
&lt;td&gt;Sparse data, especially for Swahili media&lt;/td&gt;
&lt;td&gt;Localized content discovery (books, music, news)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Information Extraction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Knowledge Graph Construction&lt;/td&gt;
&lt;td&gt;Rare Swahili corpora for entity linking&lt;/td&gt;
&lt;td&gt;Structured knowledge bases for research, government, and business&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Education &amp;amp; Literacy AI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reading Assistance&lt;/td&gt;
&lt;td&gt;Limited AI tutors or literacy tools&lt;/td&gt;
&lt;td&gt;Supporting Swahili literacy, personalized education&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Language Learning Tools&lt;/td&gt;
&lt;td&gt;Very few AI apps teaching Swahili&lt;/td&gt;
&lt;td&gt;Global Swahili learning adoption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Healthcare AI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Clinical Text Mining&lt;/td&gt;
&lt;td&gt;Almost nonexistent Swahili medical datasets&lt;/td&gt;
&lt;td&gt;Medical record processing, health insights&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Speech-based Diagnostics&lt;/td&gt;
&lt;td&gt;No datasets&lt;/td&gt;
&lt;td&gt;Remote healthcare, voice-based symptom screening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Finance &amp;amp; Business&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sentiment/Trend Analysis in Swahili&lt;/td&gt;
&lt;td&gt;Minimal coverage&lt;/td&gt;
&lt;td&gt;Market intelligence, consumer behavior analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Automated Form Processing&lt;/td&gt;
&lt;td&gt;Limited NLP for Swahili documents&lt;/td&gt;
&lt;td&gt;Banking, insurance, government services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Legal &amp;amp; Governance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Legal Document Analysis&lt;/td&gt;
&lt;td&gt;Rare datasets&lt;/td&gt;
&lt;td&gt;Contract review, policy extraction, case law research&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Automated Compliance Checks&lt;/td&gt;
&lt;td&gt;Very limited AI tools&lt;/td&gt;
&lt;td&gt;Regulatory monitoring, e-government services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Social Media &amp;amp; Content Moderation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hate Speech / Misinformation Detection&lt;/td&gt;
&lt;td&gt;Almost no labeled datasets&lt;/td&gt;
&lt;td&gt;Safer online communities, responsible platform governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Social Analytics&lt;/td&gt;
&lt;td&gt;Sparse tools&lt;/td&gt;
&lt;td&gt;Monitoring trends, public opinion, emergency response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cultural &amp;amp; Historical Preservation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Digitization of Literature&lt;/td&gt;
&lt;td&gt;Limited Swahili text corpora&lt;/td&gt;
&lt;td&gt;Preserving oral history, books, and cultural materials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Oral History Transcription&lt;/td&gt;
&lt;td&gt;Very few annotated datasets&lt;/td&gt;
&lt;td&gt;Archiving traditional storytelling and interviews&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This table already highlights &lt;strong&gt;40+ tasks&lt;/strong&gt; where Swahili is significantly underrepresented. Most of these gaps are not due to technical impossibility—they’re primarily &lt;strong&gt;data scarcity and research neglect&lt;/strong&gt;. Addressing them would have &lt;strong&gt;high societal, educational, and economic impact&lt;/strong&gt;, especially in East Africa where Swahili is widely spoken.&lt;/p&gt;

&lt;p&gt;So i am going to leave these here until i get implementations of them. &lt;/p&gt;

</description>
      <category>swahili</category>
      <category>language</category>
      <category>nlp</category>
      <category>eastafrica</category>
    </item>
    <item>
      <title>PostgreSQL Backup the quick Way</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Sat, 04 Oct 2025 19:18:54 +0000</pubDate>
      <link>https://dev.to/eddiegulay/postgresql-backup-the-quick-way-aii</link>
      <guid>https://dev.to/eddiegulay/postgresql-backup-the-quick-way-aii</guid>
      <description>&lt;p&gt;PostgreSQL is one of the most reliable open-source databases, but backups are often handled in overly complex ways — involving filesystem snapshots, replication configurations, or manual tar archives.&lt;br&gt;
If you just need a &lt;strong&gt;clean, portable, and consistent backup of all your databases&lt;/strong&gt;, PostgreSQL already includes a simple tool for that: &lt;code&gt;pg_dumpall&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This post shows how to create a full backup of your PostgreSQL server in one line — and how to move it safely to your local machine.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Why “Logical” Backups Are Enough&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;There are two kinds of PostgreSQL backups:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Physical backups&lt;/strong&gt; — copy all data files on disk; good for point-in-time recovery but heavy and less portable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logical backups&lt;/strong&gt; — dump all SQL definitions and data; lighter, portable, and perfect for everyday snapshots or migrations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your goal is to have a restorable &lt;code&gt;.sql&lt;/code&gt; file containing every database, user, and permission, a &lt;strong&gt;logical backup&lt;/strong&gt; is the right choice.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1 — Create the Backup&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Run this command directly on your server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres pg_dumpall &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/october_4_2025_backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connects as the &lt;code&gt;postgres&lt;/code&gt; superuser&lt;/li&gt;
&lt;li&gt;Dumps &lt;strong&gt;all databases&lt;/strong&gt;, &lt;strong&gt;roles&lt;/strong&gt;, and &lt;strong&gt;grants&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Produces one plain-text SQL file ready for restoration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can name the file however you prefer — for example, include the date:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/tmp/october_4_2025_backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2 — Verify the Backup&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Check that the backup file exists and has a reasonable size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; /tmp/october_4_2025_backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optional: look at the first few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;head&lt;/span&gt; /tmp/october_4_2025_backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3 — Copy the Backup to Your Local Machine&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;scp&lt;/code&gt; (secure copy) to transfer the backup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp user@your_server_ip:/tmp/october_4_2025_backup.sql ~/Downloads/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;user&lt;/code&gt; → your SSH username&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;your_server_ip&lt;/code&gt; → your server’s IP address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now your backup is safely stored on your local machine.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Optional — Compress the Backup&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If the database is large:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;gzip&lt;/span&gt; /tmp/october_4_2025_backup.sql
scp user@your_server_ip:/tmp/october_4_2025_backup.sql.gz ~/Downloads/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This typically reduces file size by 70–90%.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Step 4 — Restore the Backup (When Needed)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To restore everything to a new PostgreSQL instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-U&lt;/span&gt; postgres &lt;span class="nt"&gt;-f&lt;/span&gt; october_4_2025_backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If compressed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;gunzip&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; october_4_2025_backup.sql.gz | psql &lt;span class="nt"&gt;-U&lt;/span&gt; postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This recreates all databases, roles, and data as they were during the backup.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Automation Tip&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To schedule a daily or weekly backup automatically, create a cron job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;crontab &lt;span class="nt"&gt;-u&lt;/span&gt; postgres &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 2 * * * /usr/bin/pg_dumpall &amp;gt; /var/backups/pg_backup_$(date +\%F).sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That takes a nightly snapshot at 2 a.m. — hands-free.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>Setting up SSH for GitHub on Linux Mint</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Sun, 28 Sep 2025 21:17:54 +0000</pubDate>
      <link>https://dev.to/eddiegulay/setting-up-ssh-for-github-on-linux-mint-1n02</link>
      <guid>https://dev.to/eddiegulay/setting-up-ssh-for-github-on-linux-mint-1n02</guid>
      <description>&lt;p&gt;When working with GitHub, cloning repositories over SSH is faster and more secure than using HTTPS. If you’re setting it up for the first time, it can feel like a maze of commands and errors. This guide walks you through generating an SSH key, adding it to GitHub, and configuring your machine to authenticate seamlessly.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Check for Existing SSH Keys
&lt;/h2&gt;

&lt;p&gt;First, check if you already have SSH keys on your system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-al&lt;/span&gt; ~/.ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see files like &lt;code&gt;id_ed25519.pub&lt;/code&gt; or &lt;code&gt;id_rsa.pub&lt;/code&gt;, you already have keys. Otherwise, continue to the next step.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Generate a New SSH Key
&lt;/h2&gt;

&lt;p&gt;Generate a new Ed25519 key pair (recommended):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"your_email@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;your_email@example.com&lt;/code&gt; with the email tied to your GitHub account.&lt;/li&gt;
&lt;li&gt;When prompted for a file name, press &lt;strong&gt;Enter&lt;/strong&gt; to use the default (&lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt;), or specify your own (e.g., &lt;code&gt;mygithubkey&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you chose a custom name, move the files into &lt;code&gt;~/.ssh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; ~/path/to/mygithubkey ~/.ssh/
&lt;span class="nb"&gt;mv&lt;/span&gt; ~/path/to/mygithubkey.pub ~/.ssh/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the correct permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/mygithubkey
&lt;span class="nb"&gt;chmod &lt;/span&gt;644 ~/.ssh/mygithubkey.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Start the SSH Agent and Add the Key
&lt;/h2&gt;

&lt;p&gt;Start the SSH agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ssh-agent &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add your private key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-add ~/.ssh/mygithubkey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it was added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-add &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Add the Public Key to GitHub
&lt;/h2&gt;

&lt;p&gt;Display your public key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/mygithubkey.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAEXAMPLEKEYSTRING user@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the entire line, then go to:&lt;br&gt;
&lt;strong&gt;GitHub → Settings → SSH and GPG keys → New SSH key&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give it a title (e.g., &lt;em&gt;Linux Mint Laptop&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Paste the key.&lt;/li&gt;
&lt;li&gt;Save.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  5. Test the Connection
&lt;/h2&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-T&lt;/span&gt; git@github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is set up correctly, you’ll see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hi username! You've successfully authenticated, but GitHub does not provide shell access.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Clone Repositories with SSH
&lt;/h2&gt;

&lt;p&gt;Now you can clone repositories using the SSH URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone git@github.com:your-username/your-repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Optional: Configure SSH for Multiple Keys
&lt;/h2&gt;

&lt;p&gt;If you use multiple keys, configure SSH to always use the right one for GitHub. Edit your config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/mygithubkey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and exit. Now GitHub will always use that key automatically.&lt;/p&gt;

</description>
      <category>github</category>
      <category>tutorial</category>
      <category>security</category>
      <category>linux</category>
    </item>
    <item>
      <title>How to Transform Jupyter Notebook into a Maintainable AI Project</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Fri, 16 May 2025 20:09:51 +0000</pubDate>
      <link>https://dev.to/eddiegulay/how-to-transform-jupyter-notebook-into-a-maintainable-ai-project-3cjl</link>
      <guid>https://dev.to/eddiegulay/how-to-transform-jupyter-notebook-into-a-maintainable-ai-project-3cjl</guid>
      <description>&lt;p&gt;A Python package named &lt;a href="https://github.com/eddiegulay/modinit" rel="noopener noreferrer"&gt;modinit&lt;/a&gt; that facilitates the rapid scaffolding of AI model training repositories with a standardized, best-practice structure. This tool is designed to save time and ensure consistency across machine learning projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foccepcwszvfgyim7habl.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foccepcwszvfgyim7habl.gif" alt="modinitdemo" width="720" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem, Transitioning from Notebooks to Production Ready Code
&lt;/h3&gt;

&lt;p&gt;In machine learning and AI development, it's common to begin with Jupyter notebooks for experimentation and prototyping. However, as projects mature, there's a need to transition this code into a more structured, maintainable, and deployable format. This transition poses several &lt;/p&gt;

&lt;p&gt;challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Code Organization&lt;/strong&gt;: Notebooks often contain a mix of data processing, model definition, training loops, and evaluation code, making it hard to modularize and reuse components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Version Control&lt;/strong&gt;: Tracking changes in notebooks is less straightforward compared to plain &lt;code&gt;.py&lt;/code&gt; files, complicating collaboration and versioning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reusability and Testing&lt;/strong&gt;: Functions and classes defined in notebooks are not easily reusable across different projects or testable in isolation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deployment&lt;/strong&gt;: Deploying models trained in notebooks to production environments requires refactoring code into scripts or packages, which can be time-consuming and error-prone.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Solution, Structured Project Template
&lt;/h3&gt;

&lt;p&gt;To address these challenges, adopting a standardized project structure is essential. Such a structure separates concerns, promotes code reusability, and aligns with best practices in software development. Here's a suggested layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your_project/
│
├── notebooks/            # Jupyter notebooks for experimentation
│   └── prototype.ipynb
│
├── src/                  # Core Python package
│   ├── __init__.py
│   ├── data.py           # Data processing functions
│   ├── model.py          # Model architecture definitions
│   ├── train.py          # Training routines
│   ├── evaluate.py       # Evaluation metrics and logic
│   └── utils.py          # Utility functions (e.g., decoding, accuracy calculations)
│
├── main.py               # Entry point for training/testing/inference
├── config.yaml           # Configuration file for hyperparameters and settings
├── requirements.txt      # List of dependencies
└── README.md             # Project overview and instructions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure offers several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modularity&lt;/strong&gt;: Each component of the project (data processing, model definition, training, evaluation) is encapsulated in its own module, enhancing readability and maintainability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: As the project grows, this structure accommodates the addition of new features, models, or datasets without significant reorganization.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Collaboration&lt;/strong&gt;: Clear separation of concerns allows team members to work on different parts of the project simultaneously with minimal conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deployment&lt;/strong&gt;: Having a &lt;code&gt;main.py&lt;/code&gt; as the entry point simplifies the process of deploying the model for inference or integrating it into larger systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Introducing &lt;code&gt;modinit&lt;/code&gt;: Automating the Setup
&lt;/h3&gt;

&lt;p&gt;To streamline the creation of such a structured project, your &lt;code&gt;modinit&lt;/code&gt; package automates the scaffolding process. By running a simple command, developers can generate a project with the aforementioned layout, complete with template files and docstrings. This automation reduces setup time and ensures adherence to best practices from the outset.&lt;/p&gt;




&lt;h3&gt;
  
  
  Next Steps
&lt;/h3&gt;

&lt;p&gt;To get started with &lt;code&gt;modinit&lt;/code&gt;, you can install it via pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;modinit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, initialize a new project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;modinit create my_new_project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will generate a new directory &lt;code&gt;my_new_project&lt;/code&gt; with the standardized structure, allowing you to focus on developing your AI models without worrying about project organization.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>initialization</category>
      <category>template</category>
      <category>research</category>
    </item>
    <item>
      <title>Fed Up with Docker? Here, Uninstall It Completely (No Coming Back!)</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Wed, 26 Feb 2025 12:01:44 +0000</pubDate>
      <link>https://dev.to/eddiegulay/fed-up-with-docker-heres-how-to-uninstall-it-completely-no-coming-back-4mnb</link>
      <guid>https://dev.to/eddiegulay/fed-up-with-docker-heres-how-to-uninstall-it-completely-no-coming-back-4mnb</guid>
      <description>&lt;p&gt;Docker is a fantastic tool... until it isn’t. Maybe you're tired of mysterious container issues, endless rebuilds, or "it works on my machine" nightmares. Whatever the reason, you’ve reached the breaking point and decided to part ways with Docker. You don’t just want it gone — you want it obliterated from your system with no trace left behind.&lt;/p&gt;

&lt;p&gt;If that sounds like you, this guide is your salvation. We’re not going to sugarcoat it. This post is about &lt;strong&gt;getting rid of Docker once and for all&lt;/strong&gt;. Let’s dive in.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Why Uninstall Docker?&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Bloated disk usage from dangling images and unused volumes.&lt;/li&gt;
&lt;li&gt;Frequent conflicts and compatibility issues.&lt;/li&gt;
&lt;li&gt;Frustration with complex configurations.&lt;/li&gt;
&lt;li&gt;Seeking alternatives that better fit your workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o7c9qfyeurxs5m9n4ll.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o7c9qfyeurxs5m9n4ll.png" alt="Image description" width="750" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whatever the reason, you're here to uninstall Docker, so let's not waste time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Stop and Remove Running Containers
&lt;/h2&gt;

&lt;p&gt;Before you rip Docker out, you need to stop any running containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; | xargs &lt;span class="nt"&gt;-r&lt;/span&gt; docker stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Want to nuke all containers, images, and volumes?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--volumes&lt;/span&gt; &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; This wipes out all your Docker data. If you're sure you never want to see it again, go for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Uninstall Docker (Ubuntu/Linux)
&lt;/h2&gt;

&lt;p&gt;Docker doesn’t just live in one place. It scatters components all over your system. Here’s how to find them all and remove them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Purge Docker Packages
&lt;/h4&gt;

&lt;p&gt;Remove Docker and its related components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get purge &lt;span class="nt"&gt;-y&lt;/span&gt; docker-engine docker docker.io docker-ce docker-ce-cli containerd runc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Clean Up Residual Files
&lt;/h2&gt;

&lt;p&gt;Docker leaves behind configuration files and volumes. Nuke them with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/docker
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/containerd
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /etc/docker
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/.docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures no leftover Docker data is hiding in your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Remove Docker Repositories and Keys
&lt;/h2&gt;

&lt;p&gt;Get rid of Docker’s repository entries and GPG keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo rm&lt;/span&gt; /etc/apt/sources.list.d/docker.list
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /etc/apt/keyrings/docker.gpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove the keyrings directory if it was created just for Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /etc/apt/keyrings/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Autoremove and Clean Packages
&lt;/h2&gt;

&lt;p&gt;Run these commands to clean any orphaned packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get autoremove &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--purge&lt;/span&gt; docker docker-ce docker-ce-cli
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Verify Docker is Gone
&lt;/h2&gt;

&lt;p&gt;Let’s make sure Docker is truly out of your life:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nt"&gt;--version&lt;/span&gt;  &lt;span class="c"&gt;# Should return 'command not found'&lt;/span&gt;
docker-compose &lt;span class="nt"&gt;--version&lt;/span&gt;  &lt;span class="c"&gt;# Should also return 'command not found'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;If both commands fail, congratulations!&lt;/strong&gt; Docker is history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Remove Docker Groups and Users (Optional)
&lt;/h2&gt;

&lt;p&gt;To remove the Docker group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;groupdel docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you had a dedicated Docker user (rare case), you can delete it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;userdel docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Reboot (Optional but Recommended)
&lt;/h2&gt;

&lt;p&gt;Sometimes services linger in memory. Reboot to make sure everything is cleared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  While sipping monster energy ...
&lt;/h2&gt;

&lt;p&gt;Docker had its moment, but you’re done. With the steps above, you’ve not only uninstalled Docker — you’ve wiped every trace of it from your machine. No coming back. No lingering configs. No haunted volumes.&lt;/p&gt;

&lt;p&gt;Thinking of alternatives? Check out tools like &lt;strong&gt;Podman&lt;/strong&gt;, &lt;strong&gt;Minikube&lt;/strong&gt;, or &lt;strong&gt;Vagrant&lt;/strong&gt; for container-free virtualization. Or maybe take a break from containerization altogether. You’ve earned it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goodbye, Docker. It was... something.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;TL;DR:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Stop and remove containers: &lt;code&gt;docker system prune -a --volumes --force&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Purge Docker: &lt;code&gt;sudo apt-get purge -y docker*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Remove Docker files: &lt;code&gt;sudo rm -rf /var/lib/docker /etc/docker ~/.docker&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Clean up packages: &lt;code&gt;sudo apt-get autoremove -y --purge docker*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Verify: &lt;code&gt;docker --version&lt;/code&gt; (should fail)&lt;/li&gt;
&lt;li&gt;Reboot: &lt;code&gt;sudo reboot&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;That’s it. Docker is out. No regrets.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>fedup</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Writing clear, concise, and meaningful commit messages is key to maintaining a well-documented Git history. Here’s a practical guide to writing great Git commit messages.</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Wed, 29 Jan 2025 22:31:52 +0000</pubDate>
      <link>https://dev.to/eddiegulay/writing-clear-concise-and-meaningful-commit-messages-is-key-to-maintaining-a-well-documented-git-1gp6</link>
      <guid>https://dev.to/eddiegulay/writing-clear-concise-and-meaningful-commit-messages-is-key-to-maintaining-a-well-documented-git-1gp6</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/eddiegulay" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1162732%2Fa728d062-8f1d-4b0f-a055-2672e229f637.png" alt="eddiegulay"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/eddiegulay/writing-clear-concise-and-meaningful-commit-messages-for-a-well-documented-git-history-kkc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Writing clear, concise, and meaningful commit messages for a well-documented Git history&lt;/h2&gt;
      &lt;h3&gt;Eddie Gulay ・ Jan 29&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#github&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#commitmessage&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#git&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>github</category>
      <category>commitmessage</category>
      <category>git</category>
    </item>
    <item>
      <title>Writing clear, concise, and meaningful commit messages for a well-documented Git history</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Wed, 29 Jan 2025 22:28:40 +0000</pubDate>
      <link>https://dev.to/eddiegulay/writing-clear-concise-and-meaningful-commit-messages-for-a-well-documented-git-history-kkc</link>
      <guid>https://dev.to/eddiegulay/writing-clear-concise-and-meaningful-commit-messages-for-a-well-documented-git-history-kkc</guid>
      <description>&lt;h3&gt;
  
  
  Let’s Talk About Writing Commit Messages That Actually Make Sense
&lt;/h3&gt;

&lt;p&gt;Commit messages might seem like a small thing when you’re knee-deep in code, but they’re like little breadcrumbs you leave for yourself and your team. A good one can save you from scratching your head weeks later, wondering what on earth you were doing. So, how do you write one that’s clear and helpful without feeling like a chore? It’s simpler than you might think.&lt;/p&gt;

&lt;p&gt;Start with a &lt;strong&gt;type&lt;/strong&gt;—a quick label that says what kind of change you’re making. Then add a short summary, and if it’s a bigger change, toss in a few extra details. That’s it! Let’s break it down a bit.&lt;/p&gt;




&lt;h4&gt;
  
  
  Kick Things Off with a Commit Type
&lt;/h4&gt;

&lt;p&gt;Think of the commit type as a way to sort your changes into buckets. Here’s a quick rundown of the common ones:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;What It Means&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;feat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adding something new&lt;/td&gt;
&lt;td&gt;&lt;code&gt;feat: add date filtering for GitHub activity&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Squashing a bug&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fix: handle API failure when GitHub is down&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;refactor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tidying up code without changing what it does&lt;/td&gt;
&lt;td&gt;&lt;code&gt;refactor: optimize API response formatting&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;docs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Updating documentation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docs: update README with API usage guide&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;chore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Little maintenance tasks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chore: add .gitignore and remove logs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;style&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Just making things look prettier&lt;/td&gt;
&lt;td&gt;&lt;code&gt;style: format code with Black&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;test&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adding or tweaking tests&lt;/td&gt;
&lt;td&gt;&lt;code&gt;test: add unit tests for date filtering&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These labels make it easy to skim your commit history and know what’s what. No need for anything fancy—just pick one and go.&lt;/p&gt;




&lt;h4&gt;
  
  
  Keep Your Summary Short and Sweet
&lt;/h4&gt;

&lt;p&gt;Next up is the summary. Keep it quick—aim for under 50 characters if you can, but definitely cap it at 72. Write it like you’re giving a command: “Add this” or “Fix that.” It’s a quirky little standard in coding, but it works. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;feat: add API endpoint for fetching GitHub activity&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fix: resolve bug in date filtering logic&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docs: update README with setup instructions&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid stuff like “Added a thing” or “Fixing bugs”—it’s too vague or wishy-washy. Be specific, and you’ll thank yourself later.&lt;/p&gt;




&lt;h4&gt;
  
  
  Add Some Details When It Matters
&lt;/h4&gt;

&lt;p&gt;For bigger changes, a short summary might not cut it. That’s where a detailed description comes in. After your type and summary, leave a blank line, then add a few lines about what you did and why. Here’s one I’d write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat: add GitHub activity API

- Fetches user events from GitHub API
- Supports filtering by single date or date range
- Caches results for faster responses

Closes #12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I remember when I first started coding, I’d skip this part entirely. My commit messages were just “updated code” or something equally useless. Weeks later, I’d be lost. Now, I love adding these little notes—especially for tricky fixes or big features. It’s like leaving a cheat sheet for my future self.&lt;/p&gt;




&lt;h4&gt;
  
  
  Tie It to Issues or Shout Out Your Team
&lt;/h4&gt;

&lt;p&gt;If your commit ties back to an issue or pull request, mention it. Something like &lt;code&gt;Closes #22&lt;/code&gt; or &lt;code&gt;Related to #15&lt;/code&gt; keeps everything connected. And if you’re working with others, give them a nod with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat: add webhook support for GitHub activity

Co-authored-by: Edgar Gulay &amp;lt;edgargulay@outlook.com&amp;gt;
Co-authored-by: John Doe &amp;lt;johndoe@example.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a nice way to share the credit.&lt;/p&gt;




&lt;h4&gt;
  
  
  Keep Your History Tidy
&lt;/h4&gt;

&lt;p&gt;We’ve all been guilty of sloppy commits at some point. You know the ones: &lt;code&gt;wip: working on it&lt;/code&gt; or &lt;code&gt;fix: bug fixes&lt;/code&gt;. What were you working on? Which bug? Let’s avoid that, shall we? Instead, try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fix: correct timestamp format in API response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s specific, it’s clear, and it won’t leave anyone guessing. Your commit history should tell a story, not a mystery.&lt;/p&gt;




&lt;h4&gt;
  
  
  A Little Bonus Trick
&lt;/h4&gt;

&lt;p&gt;If you’re into automating things, you can set up a Git hook to nudge you into writing good commit messages. Here’s a quick way to do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'exec &amp;lt; /dev/tty &amp;amp;&amp;amp; git cz --hook || true'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git/hooks/commit-msg
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x .git/hooks/commit-msg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s like having a friendly reminder pop up every time you commit. Not required, but it’s a neat little helper.&lt;/p&gt;




&lt;h3&gt;
  
  
  Wrapping It Up
&lt;/h3&gt;

&lt;p&gt;So, there you go! Commit messages don’t have to be a drag. Use a type, keep your summary short and punchy, add details when you need to, and avoid the vague stuff. They’re like little notes to your future self and your team—make them count. Happy coding!&lt;/p&gt;




</description>
      <category>github</category>
      <category>commitmessage</category>
      <category>git</category>
    </item>
    <item>
      <title>Moving flask app to production.</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Thu, 16 Jan 2025 22:51:50 +0000</pubDate>
      <link>https://dev.to/eddiegulay/moving-flask-app-to-production-41b6</link>
      <guid>https://dev.to/eddiegulay/moving-flask-app-to-production-41b6</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/eddiegulay" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1162732%2Fa728d062-8f1d-4b0f-a055-2672e229f637.png" alt="eddiegulay"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/eddiegulay/deploying-flask-app-in-digital-ocean-droplet-cep" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Deploying Flask App in Digital Ocean Droplet&lt;/h2&gt;
      &lt;h3&gt;Eddie Gulay ・ Jan 16&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#production&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#flask&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#generative&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#gemini&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>flask</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Deploying Flask App in Digital Ocean Droplet</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Thu, 16 Jan 2025 22:50:29 +0000</pubDate>
      <link>https://dev.to/eddiegulay/deploying-flask-app-in-digital-ocean-droplet-cep</link>
      <guid>https://dev.to/eddiegulay/deploying-flask-app-in-digital-ocean-droplet-cep</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1wfygnml9nlmepp52jea.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1wfygnml9nlmepp52jea.jpg" alt="to production with flask in a droplet" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recently, I read a blog post about customizing APIs from Gemini Models, but when I tried following the instructions, everything got stuck at localhost:5000. Basically, my apps were only visible on my computer. So, I went on a quest to figure out how to make it accessible to everyone. That's when I learned about deploying Flask apps on a Digital Ocean droplet. This guide is like a map to help you do the same, so your projects don't get stuck at localhost:5000 either. Let's share your awesome creations with the world! ✨ It's time to unleash them onto the global stage by mastering the art of deploying Flask applications on a Digital Ocean droplet or any &lt;strong&gt;Ubuntu Server.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Step by step  guide to deploying Flask app in digital ocean droplet using gunicorn and nginx.&lt;/p&gt;

&lt;p&gt;Things we are going to cover&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating digital ocean droplet&lt;/li&gt;
&lt;li&gt;Preparing environment&lt;/li&gt;
&lt;li&gt;Actual deployment guide&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Creating digital ocean droplet
&lt;/h2&gt;

&lt;p&gt;Go to digital ocean and create an account if you don't have one. here is the link &lt;br&gt; ✨ &lt;a href="https://m.do.co/c/a8690363c67d" rel="noopener noreferrer"&gt;Digital Ocean&lt;/a&gt; 🎉 ~ This is a referral link, you will get &lt;strong&gt;$200&lt;/strong&gt; credit for &lt;strong&gt;60 days&lt;/strong&gt;. so no more excuses to why your projects are not deployed.&lt;/p&gt;

&lt;p&gt;After creating an account, click on the create button and select droplets. You will be presented with a list of options. Select the Ubuntu 20.04 LTS. Choose the plan of your preference. You can choose any data center region you want. I usually choose the one closest to me.&lt;/p&gt;

&lt;p&gt;Choose the authentication method. SSH keys are the best but for this guide, we are going to use a password. You can always add SSH keys later.&lt;/p&gt;

&lt;p&gt;if you are a windows user get yourself a virtual box and install Ubuntu. or use WSL 😂.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing environment
&lt;/h2&gt;

&lt;p&gt;Now that we have our droplet up and running, let's prepare our environment.&lt;br&gt;
connect to your droplet using ssh.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh root@your_droplet_ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vfeuhvh9s3ijktgx5x7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vfeuhvh9s3ijktgx5x7.png" alt="ssh init" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide your password and you are in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update and upgrade
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the command sudo apt update refreshes the list of available software packages on your system, ensuring that it has the latest information from the repositories. Following that, sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools installs essential tools and dependencies for Python development. This includes the Python 3 package manager (python3-pip), development headers for Python (python3-dev), fundamental build tools (build-essential), as well as libraries for SSL and FFI (libssl-dev and libffi-dev), and the Python package distribution tools (python3-setuptools).&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a virtual environment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3-venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a directory for your project and navigate to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;apps
&lt;span class="nb"&gt;cd &lt;/span&gt;apps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a virtual environment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Activate the virtual environment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Clone your project
&lt;/h3&gt;

&lt;p&gt;let's call our project &lt;code&gt;flask_app&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone your_project_url.git
&lt;span class="nb"&gt;cd &lt;/span&gt;flask_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install project dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; flask_app/requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now that we have your project up and running, let's run it and see if everything is working as expected.&lt;br&gt;
first lets allow the port we are going to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 5000

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

&lt;/div&gt;



&lt;p&gt;run the app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if everything is working as expected, you should be able to access your app using your droplet ip and port 5000.&lt;br&gt;
something like this &lt;code&gt;http://your_droplet_ip:5000&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Actual deployment guide
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create WSGI entry point
&lt;/h3&gt;

&lt;p&gt;Create a file called &lt;code&gt;wsgi.py&lt;/code&gt; in your project root directory.&lt;br&gt;
or just copy app.py to wsgi.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;app.py wsgi.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure Gunicorn
&lt;/h3&gt;

&lt;p&gt;Safe check if gunicorn is installed&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gunicorn &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if not installed install it using pip&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;gunicorn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;try running your app using gunicorn&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gunicorn &lt;span class="nt"&gt;--bind&lt;/span&gt; 0.0.0.0:5000 wsgi:app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check your app using your droplet ip and port 5000. something like this &lt;code&gt;http://your_droplet_ip:5000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;if everything is working as expected, let's move to the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a systemd service file
&lt;/h3&gt;

&lt;p&gt;Create a systemd service file for gunicorn, this will allow gunicorn to automatically start on boot.&lt;br&gt;
first deactivate your virtual environment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deactivate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a file called &lt;code&gt;flask_app.service&lt;/code&gt; in &lt;code&gt;/etc/systemd/system/&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/systemd/system/flask_app.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following configuration to the file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;Unit]
&lt;span class="nv"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Gunicorn instance to serve flask_app
&lt;span class="nv"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;network.target

&lt;span class="o"&gt;[&lt;/span&gt;Service]
&lt;span class="nv"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root
&lt;span class="nv"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;www-data
&lt;span class="nv"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/root/apps/flask_app
&lt;span class="nv"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"PATH=/root/apps/venv/bin"&lt;/span&gt;
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/root/apps/venv/bin/gunicorn &lt;span class="nt"&gt;--workers&lt;/span&gt; 3 &lt;span class="nt"&gt;--bind&lt;/span&gt; 0.0.0.0:5000 &lt;span class="nt"&gt;-m&lt;/span&gt; 777 wsgi:app

&lt;span class="o"&gt;[&lt;/span&gt;Install]
&lt;span class="nv"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After creating the file, start the gunicorn service and enable it to start on boot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start flask_app
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;flask_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the status of the service to make sure it's running without any issues.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status flask_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;example output&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lxqr73j9uh1t0yj78i1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lxqr73j9uh1t0yj78i1.png" alt="alt text" width="800" height="170"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Configure Nginx to Proxy Requests
&lt;/h3&gt;

&lt;p&gt;Install Nginx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new server block configuration file in Nginx's &lt;code&gt;sites-available&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/flask_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following configuration to the file. Replace &lt;code&gt;your_domain_or_ip&lt;/code&gt; with your actual domain name or IP address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;server &lt;span class="o"&gt;{&lt;/span&gt;
    listen 80&lt;span class="p"&gt;;&lt;/span&gt;
    server_name 143.198.232.28&lt;span class="p"&gt;;&lt;/span&gt;

    location / &lt;span class="o"&gt;{&lt;/span&gt;
        proxy_pass https://143.198.232.28:5000&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a symbolic link to the file in the &lt;code&gt;sites-enabled&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/flask-app /etc/nginx/sites-enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test your Nginx configuration for syntax errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you should see something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf &lt;span class="nb"&gt;test &lt;/span&gt;is successful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the test is successful, restart Nginx.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember we allowed port 5000 earlier, now we can remove it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw delete allow 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you should be able to access your app using your domain name or IP address without the port number. something like this &lt;code&gt;http://your_droplet_ip&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Secure your app with SSL
&lt;/h3&gt;

&lt;p&gt;Install certbot&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obtain a free SSL certificate for your domain using certbot if you have a domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; your_domain_or_ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Certbot will ask you to provide an email address for lost key recovery and notices, and to agree to the terms of service. After doing so, certbot will communicate with the Let's Encrypt server, then run a challenge to verify that you control the domain you're requesting a certificate for.&lt;/p&gt;

&lt;p&gt;When that's finished, certbot will ask how you'd like to configure your HTTPS settings.&lt;/p&gt;

&lt;p&gt;or you can use openssl to generate a self-signed certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:2048 &lt;span class="nt"&gt;-keyout&lt;/span&gt; /etc/ssl/private/flask_app.key &lt;span class="nt"&gt;-out&lt;/span&gt; /etc/ssl/certs/flask_app.crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new server block configuration file in Nginx's &lt;code&gt;sites-available&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/flask_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following configuration to the file. Replace &lt;code&gt;your_domain_or_ip&lt;/code&gt; with your actual domain name or IP address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
server &lt;span class="o"&gt;{&lt;/span&gt;
    listen 80&lt;span class="p"&gt;;&lt;/span&gt;
    server_name

    location / &lt;span class="o"&gt;{&lt;/span&gt;
        proxy_pass https://your_domain_or_ip&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

server &lt;span class="o"&gt;{&lt;/span&gt;
    listen 443 ssl&lt;span class="p"&gt;;&lt;/span&gt;
    server_name your_domain_or_ip&lt;span class="p"&gt;;&lt;/span&gt;

    ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt&lt;span class="p"&gt;;&lt;/span&gt;
    ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key&lt;span class="p"&gt;;&lt;/span&gt;

    location / &lt;span class="o"&gt;{&lt;/span&gt;
        proxy_pass https://your_domain_or_ip&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration tells Nginx to listen on both port 80 (HTTP) and port 443 (HTTPS). It uses the self-signed certificate and private key that you created.&lt;/p&gt;

&lt;p&gt;After updating the Nginx configuration, remember to test the configuration and reload or restart Nginx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;nginx -t&lt;/code&gt; command checks the configuration for syntax errors. The &lt;code&gt;systemctl reload nginx&lt;/code&gt; command reloads the Nginx configuration without interrupting currently connected clients.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Please note that because the certificate is self-signed, browsers will show a warning to users that the site is not secure. Users will need to manually accept the risk and proceed to the site.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Hopefully with this guide, you were able to deploy your flask app in a digital ocean droplet. Your projects are way too precious to be running on localhost. Deploy them and share them with the world. If you have any questions or suggestions, feel free to reach out help is everywhere. ✨&lt;/p&gt;

</description>
      <category>production</category>
      <category>flask</category>
      <category>generative</category>
      <category>gemini</category>
    </item>
    <item>
      <title>Enhancing your web app's search with OctoPalm.js—a lightweight JavaScript library offering real-time, customizable search functionality. Features include animated results, emoji support, and cross-browser compatibility.</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Thu, 09 Jan 2025 14:42:52 +0000</pubDate>
      <link>https://dev.to/eddiegulay/enhancing-your-web-apps-search-with-octopalmjs-a-lightweight-javascript-library-offering-bk0</link>
      <guid>https://dev.to/eddiegulay/enhancing-your-web-apps-search-with-octopalmjs-a-lightweight-javascript-library-offering-bk0</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/eddiegulay" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1162732%2Fa728d062-8f1d-4b0f-a055-2672e229f637.png" alt="eddiegulay"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/eddiegulay/javascript-library-to-add-real-time-customizable-search-to-your-web-applications-gmk" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;OctoPalm.js || JavaScript library to add real-time, customizable search to your web applications.&lt;/h2&gt;
      &lt;h3&gt;Eddie Gulay ・ Sep 19 '24&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#vscode&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#realtime&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#search&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Building OpenCV 4.10.0 with GUI Support in WSL</title>
      <dc:creator>Eddie Gulay</dc:creator>
      <pubDate>Sun, 29 Dec 2024 21:04:34 +0000</pubDate>
      <link>https://dev.to/eddiegulay/building-opencv-4100-with-gui-support-in-wsl-5do0</link>
      <guid>https://dev.to/eddiegulay/building-opencv-4100-with-gui-support-in-wsl-5do0</guid>
      <description>&lt;p&gt;If you’re working with OpenCV on WSL and hit the infamous &lt;code&gt;cv2.error&lt;/code&gt; stating &lt;em&gt;GUI: NONE&lt;/em&gt;, you’ve probably realized that your OpenCV installation lacks GUI backend support. This guide will walk you through building OpenCV 4.10.0 from source with full GUI support, ensuring functions like &lt;code&gt;cv2.imshow&lt;/code&gt; work seamlessly. Let’s dive in!&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, make sure you have WSL set up (preferably WSL2) with a Linux distribution like Ubuntu. We’ll also need to install several dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install Required Dependencies
&lt;/h3&gt;

&lt;p&gt;Open your terminal and run the following commands to install the necessary development tools and libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; build-essential cmake git pkg-config
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libjpeg-dev libpng-dev libtiff-dev
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libavcodec-dev libavformat-dev libswscale-dev
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libv4l-dev v4l-utils
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libxvidcore-dev libx264-dev
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libgtk2.0-dev libgtk-3-dev libcanberra-gtk-module libcanberra-gtk3-module
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python3-dev python3-pip python3-numpy
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libopenblas-dev libatlas-base-dev liblapack-dev gfortran
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libhdf5-dev libprotobuf-dev protobuf-compiler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These libraries ensure that OpenCV can handle image processing, video decoding, and GUI rendering.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2: Download OpenCV and Contrib Modules
&lt;/h3&gt;

&lt;p&gt;To build OpenCV from source, we need both the main OpenCV repository and the additional contrib modules for extended functionality.&lt;/p&gt;

&lt;h4&gt;
  
  
  Clone OpenCV:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/opencv/opencv.git
&lt;span class="nb"&gt;cd &lt;/span&gt;opencv
git checkout 4.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Clone the Contrib Modules:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
git clone https://github.com/opencv/opencv_contrib.git
&lt;span class="nb"&gt;cd &lt;/span&gt;opencv_contrib
git checkout 4.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 3: Build OpenCV
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Create a Build Directory
&lt;/h4&gt;

&lt;p&gt;Navigate back to the OpenCV directory and create a &lt;code&gt;build&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../opencv
&lt;span class="nb"&gt;mkdir &lt;/span&gt;build
&lt;span class="nb"&gt;cd &lt;/span&gt;build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Configure the Build
&lt;/h4&gt;

&lt;p&gt;Run &lt;code&gt;cmake&lt;/code&gt; to configure the OpenCV build, ensuring GUI support is enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cmake &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nv"&gt;CMAKE_BUILD_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Release &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nv"&gt;CMAKE_INSTALL_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nv"&gt;OPENCV_EXTRA_MODULES_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;../../opencv_contrib/modules &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nv"&gt;WITH_GTK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ON &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nv"&gt;WITH_OPENGL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ON &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nv"&gt;BUILD_EXAMPLES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ON ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Compile OpenCV
&lt;/h4&gt;

&lt;p&gt;Use the following command to compile OpenCV. This process can take some time depending on your system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make &lt;span class="nt"&gt;-j&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;$(nproc)&lt;/code&gt; ensures all available CPU cores are used for compilation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Install OpenCV
&lt;/h4&gt;

&lt;p&gt;Once the compilation is complete, install OpenCV on your system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;make &lt;span class="nb"&gt;install
sudo &lt;/span&gt;ldconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 4: Verify the Installation
&lt;/h3&gt;

&lt;p&gt;Let’s check if OpenCV is installed correctly and GUI support is enabled:&lt;/p&gt;

&lt;h4&gt;
  
  
  Run a Python Script
&lt;/h4&gt;

&lt;p&gt;Open Python and run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBuildInformation&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for the &lt;strong&gt;GUI&lt;/strong&gt; section in the output. It should list &lt;code&gt;GTK&lt;/code&gt; or similar. If it says &lt;code&gt;NONE&lt;/code&gt;, ensure the required libraries were installed before running &lt;code&gt;cmake&lt;/code&gt; and repeat the build process.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 5: Test &lt;code&gt;cv2.imshow&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;To confirm everything works as expected, try displaying an image:&lt;/p&gt;

&lt;h4&gt;
  
  
  Sample Python Script:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;

&lt;span class="c1"&gt;# Load an image
&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path_to_your_image.jpg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Display the image
&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imshow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Test Image&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroyAllWindows&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a window pops up showing your image, congratulations! You’ve successfully built OpenCV with GUI support.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Troubleshooting Tips&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GUI: NONE Still Appears:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure you installed &lt;code&gt;libgtk2.0-dev&lt;/code&gt; and &lt;code&gt;libgtk-3-dev&lt;/code&gt; before running &lt;code&gt;cmake&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Delete the &lt;code&gt;build&lt;/code&gt; directory and re-run the &lt;code&gt;cmake&lt;/code&gt; and &lt;code&gt;make&lt;/code&gt; steps.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Errors During Compilation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check for missing dependencies in the error messages and install them.&lt;/li&gt;
&lt;li&gt;Ensure your system has enough memory; close other programs if necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Display Issues in WSL:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use an X server like &lt;a href="https://sourceforge.net/projects/vcxsrv/" rel="noopener noreferrer"&gt;VcXsrv&lt;/a&gt; on Windows if you’re not using WSLg.&lt;/li&gt;
&lt;li&gt;Export your display:
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DISPLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;.local:0
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Highlight&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Building OpenCV 4.10.0 from source may seem daunting, but it’s worth the effort for the flexibility and customization it offers. Whether you’re working on image processing, computer vision, or machine learning projects, having a fully functional OpenCV installation will unlock countless possibilities.&lt;/p&gt;

&lt;p&gt;Got stuck? Drop your errors or questions in the comments—I’m here to help!&lt;br&gt;
Happy coding!&lt;/p&gt;




&lt;p&gt;&lt;a href="https://buymeacoffee.com/eddiegulay" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8jhq9u1ovourbj8jeiw8.png" alt="Buy Eddie a coffee" width="545" height="153"&gt;&lt;/a&gt; &lt;/p&gt;

</description>
      <category>opencv</category>
      <category>wsl</category>
      <category>ubuntu</category>
      <category>make</category>
    </item>
  </channel>
</rss>
