<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.cybershu.eu/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.cybershu.eu/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-09-07T16:01:03+02:00</updated><id>https://www.cybershu.eu/feed.xml</id><title type="html">CyberShu</title><subtitle>Generalist Blog IT
</subtitle><author><name>Michał Mazur</name></author><entry xml:lang="en"><title type="html">Think Before You Build: Side Project Economics in the AI Era</title><link href="https://www.cybershu.eu/think-before-you-build-side-project-economics.html" rel="alternate" type="text/html" title="Think Before You Build: Side Project Economics in the AI Era" /><published>2026-09-07T00:00:00+02:00</published><updated>2026-09-07T00:00:00+02:00</updated><id>https://www.cybershu.eu/think-before-you-build-side-project-economics</id><content type="html" xml:base="https://www.cybershu.eu/think-before-you-build-side-project-economics.html"><![CDATA[<p>AI can turn a rough idea into working code before the coffee gets cold. It makes coding cheaper and faster, but it does not make a side project free. The project still charges for time, attention, tokens, compute, and maintenance.</p>

<p>The dangerous sentence is no longer “This will take months.” It is “I could make that vibecoding this weekend.” 
<!--more--></p>

<h2 id="no-free-lunch">No free lunch</h2>

<p>Code is cheap now. It almost became commodity. Models can draft components, explain APIs, generate tests, run pentests and patiently revisit the same bug. That is genuine leverage.</p>

<p>My real budget starts with time and attention spent defining, checking, integrating, and correcting the result. Tokens, compute, hosting, storage, and external services belong there too.</p>

<p>Then there is cognitive load:</p>
<ul>
  <li>the mental effort of keeping requirements, code, tools, and unfinished decisions in working memory.<sup id="fnref:cognitive-load" role="doc-noteref"><a href="#fn:cognitive-load" class="footnote" rel="footnote">1</a></sup></li>
  <li>Finally, there is maintenance: updates, failures, security fixes, migrations, and support after the interesting part ends.<sup id="fnref:maintenance" role="doc-noteref"><a href="#fn:maintenance" class="footnote" rel="footnote">2</a></sup></li>
</ul>

<p>Every project consumes something, including my remaining sanity. The important question is whether the project ROI (return of investment) justifies all kinds of expenses.</p>

<h2 id="test-it">Test it!</h2>

<p>Before I open an vibe tool, I try to make the problem survive a short brainstorming. This is the point where enthusiasm has to produce evidence. I ask:</p>

<ul>
  <li>What problem does this solve, stated without mentioning a framework or model?</li>
  <li>What do I gain from it: time, money, attention, or a capability I cannot obtain elsewhere?</li>
  <li>How much am I willing to spend, including the cost of maintaining it later?</li>
  <li>What must the result actually do, and what can it deliberately ignore?</li>
</ul>

<p>These questions are basic. That is useful, because side projects often become complicated long before they become necessary.</p>

<p>My rule is simple: <strong>if I cannot explain the problem, I do not need a repository yet.</strong> I need a clearer sentence. Git is excellent at tracking changes, but it cannot explain why the project exists.</p>

<h3 id="price-the-outcome-not-the-excitement">Price the outcome, not the excitement</h3>

<p>The expected gain needs to be concrete enough to compare with the cost. “I will learn something” can be a valid outcome, but then the project is an educational expense. It should not be disguised as a productivity system.</p>

<p>The same applies to saving time. An automation that removes a repetitive task may be worth building. An automation that saves some clicks while creating a permanent monitoring obligation has simply moved the work into a less visible drawer.</p>

<h2 id="check-whether-the-market-already-solved-the-problem">Check whether the market already solved the problem</h2>

<p>Once the problem is clear, I look outside my own toolbox. Buying an existing solution is also a technical decision, and its maintenance model belongs in the comparison.</p>

<p>I could build my own todo application. Instead, I pay for <a href="https://www.todoist.com/">Todoist</a>. A good product already exists, the subscription costs less than maintaining my version, and my time remains available for problems I actually want to own.</p>

<p>That last word matters: own. Building software means owning its awkward edge cases, data migrations, broken integrations, and the weekends when a dependency decides it has developed a evil personality.</p>

<p>A subscription is not automatically cheaper. The comparison should include the full cost on both sides, not a monthly price on one side and optimistic enthusiasm on the other.</p>

<h2 id="if-i-build-code-starts-fourth">If I build, code starts fourth</h2>

<p>Sometimes the market check still points toward building because the available products do not meet the stated requirement. Fine. I still do not start with code. My sequence is:</p>

<ol>
  <li><strong>Brainstorming and product engineering.</strong> Define the target user, problem, constraints, and smallest useful outcome.</li>
  <li><strong>Requirements and assumptions.</strong> Write down expected behavior, exclusions, dependencies, and claims that still need testing.</li>
  <li><strong>Architecture and tool selection.</strong> Choose the simplest structure that fits the requirements, including where data lives and what can fail.</li>
  <li><strong>Code.</strong> Implement only after the earlier decisions have reduced the number of attractive wrong turns.</li>
</ol>

<p>AI makes this order more important, not less. A model can generate a great deal of plausible code for a poorly framed problem. Speed is impressive right up to the moment it accelerates in the wrong direction.</p>

<p>I use <a href="https://github.com/obra/superpowers">Superpowers</a> to add structure to this process. I also use a “grill me” prompt that asks annoying questions early: What is missing? Which assumption is unsupported? What happens when the happy path takes a day off?</p>

<p>Those questions are cheaper before implementation. After implementation, they tend to arrive wearing bug reports.</p>

<h2 id="use-the-smallest-tool-that-closes-the-loop">Use the smallest tool that closes the loop</h2>

<p>There is one final check before a custom application earns its repository. Could <a href="https://www.make.com/en/product">Make</a>, <a href="https://n8n.io/">n8n</a>, or <a href="https://zapier.com/">Zapier</a> connect the systems and handle the workflow? Could <a href="https://www.microsoft.com/en-us/microsoft-365/excel">Microsoft Excel</a> store the data, run the calculation, and produce the output?</p>

<p>This is not an argument against engineering. It is an argument for matching the solution to the problem. A spreadsheet that works is more useful than a distributed system waiting for its first user.</p>

<p>A custom build earns its cost only when ownership creates a real advantage. In other cases, an existing product or a small automation may deliver the result with less ongoing work.</p>

<p>The point is not to suppress side projects. It is to spend scarce time and attention on the ones that deserve to exist. AI has lowered the price of producing code, but judgment still decides whether the code is an asset or a new recurring task.</p>

<p><strong>What have you built that should have been a subscription or a spreadsheet?</strong></p>

<p>P.S.
If you are interested in how to build cheap applications/SaaS, I totally recommend my last presentation at <a href="/javeloper-2026-zrob-cebulowego-sassa-notatki-prelegenta.html">Javeloper 2026</a>.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:cognitive-load" role="doc-endnote">
      <p>Lucian José Gonçales, Kleinner Farias, and Bruno C. da Silva, <a href="https://doi.org/10.1016/j.infsof.2021.106563">“Measuring the cognitive load of software developers: An extended Systematic Mapping Study”</a>, <em>Information and Software Technology</em>, 2021. <a href="#fnref:cognitive-load" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:maintenance" role="doc-endnote">
      <p>Sam Ramanujan and Sridhar Nerur, <a href="https://doi.org/10.1108/13287260910955093">“An exploratory analysis of the state of software maintenance research”</a>, <em>Journal of Systems and Information Technology</em>, 2009. <a href="#fnref:maintenance" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Michał Mazur</name></author><category term="ai" /><category term="side-project" /><category term="economy" /><category term="software-engineering" /><category term="productivity" /><summary type="html"><![CDATA[AI can turn a rough idea into working code before the coffee gets cold. It makes coding cheaper and faster, but it does not make a side project free. The project still charges for time, attention, tokens, compute, and maintenance. The dangerous sentence is no longer “This will take months.” It is “I could make that vibecoding this weekend.”]]></summary></entry><entry xml:lang="en"><title type="html">Zrób se cebulowego SaaSa, czyli cebularstwo stosowane w projektach IT - notatki prelegenta</title><link href="https://www.cybershu.eu/javeloper-2026-zrob-cebulowego-sassa-notatki-prelegenta.html" rel="alternate" type="text/html" title="Zrób se cebulowego SaaSa, czyli cebularstwo stosowane w projektach IT - notatki prelegenta" /><published>2026-05-14T00:00:00+02:00</published><updated>2026-05-14T00:00:00+02:00</updated><id>https://www.cybershu.eu/javeloper-cebula</id><content type="html" xml:base="https://www.cybershu.eu/javeloper-2026-zrob-cebulowego-sassa-notatki-prelegenta.html"><![CDATA[<p>Na stronie znajdziesz moja prezentacje z konferencji <a href="https://javeloper.pl/#okonferencji">“Javeloper 2026”</a> jak i myślę przydatne linki uzupełniające.</p>

<h2 id="prezentacja">Prezentacja</h2>
<p><strong>PDF:</strong>
<a href="/assets/docs/prezentacja.pdf">prezentacja</a></p>

<h2 id="jakie-są-twoje-wrazenia-z-prezentacji">Jakie są Twoje wrazenia z prezentacji?</h2>
<iframe data-tally-src="https://tally.so/embed/obK4Me?alignLeft=1&amp;hideTitle=1&amp;transparentBackground=1&amp;dynamicHeight=1&amp;formEventsForwarding=1" loading="lazy" width="100%" height="1395" frameborder="0" marginheight="0" marginwidth="0" title="Javeloper 2026- Twoje wrażenia"></iframe>
<script>var d=document,w="https://tally.so/widgets/embed.js",v=function(){"undefined"!=typeof Tally?Tally.loadEmbeds():d.querySelectorAll("iframe[data-tally-src]:not([src])").forEach((function(e){e.src=e.dataset.tallySrc}))};if("undefined"!=typeof Tally)v();else if(d.querySelector('script[src="'+w+'"]')==null){var s=d.createElement("script");s.src=w,s.onload=v,s.onerror=v,d.body.appendChild(s);}</script>

<h2 id="linki">Linki</h2>

<h3 id="społeczności">Społeczności</h3>

<ul>
  <li><a href="https://www.reddit.com/r/homelab/">r/homelab</a> - główna społeczność homelabbers</li>
  <li><a href="https://www.reddit.com/r/VPS/">r/VPS</a></li>
  <li><a href="https://www.reddit.com/r/sysadmin/">r/sysadmin</a></li>
</ul>

<h3 id="osoby-i-profile">Osoby i profile</h3>

<ul>
  <li><a href="https://x.com/levelsio">Pieter Levels na X</a></li>
</ul>

<h3 id="homelab-i-self-hosting">Homelab i self-hosting</h3>

<ul>
  <li><a href="https://thehomelabber.com/">The Homelabber</a></li>
  <li><a href="https://selfh.st/">selfh.st - Self-hosted content and software</a></li>
  <li><a href="https://github.com/awesome-selfhosted/awesome-selfhosted">Awesome Self-Hosted</a></li>
  <li><a href="https://mikr.us/">Mikr.us</a></li>
  <li><a href="https://www.hetzner.com/cloud/">Hetzner Cloud</a></li>
</ul>

<h3 id="tanie-vps-y-i-darmowe-limity">Tanie VPS-y i darmowe limity</h3>

<ul>
  <li><a href="https://vpspricetracker.com/">VPS Price Tracker: Cheap VPS Lists &amp; Prices</a></li>
  <li><a href="https://freetier.co/">freetier.co</a></li>
  <li><a href="https://www.freetiers.com">freetiers.com</a></li>
  <li><a href="https://free-for.dev/#/">Free for Developers</a></li>
</ul>

<h3 id="chmury-i-platformy">Chmury i platformy</h3>

<ul>
  <li><a href="https://azure.microsoft.com/en-gb/pricing/purchase-options/azure-account?icid=azurefreeaccount#freeservices">Azure - darmowe limity</a></li>
  <li><a href="https://cloud.google.com/free/docs/free-cloud-features#free-tier">Google Cloud Free Program</a></li>
  <li><a href="https://cloud.google.com/run">Google Cloud Run</a></li>
  <li><a href="https://www.oracle.com/uk/cloud/free/#always-free">Oracle Cloud Always Free</a></li>
  <li><a href="https://www.ibm.com/products/cloud/free">IBM Cloud Free Tier</a></li>
  <li><a href="https://vercel.com/">Vercel</a> - <a href="https://vercel.com/pricing">pricing</a></li>
  <li><a href="https://www.netlify.com/">Netlify</a> - <a href="https://www.netlify.com/pricing/">pricing and plans</a></li>
  <li><a href="https://railway.com/">Railway</a></li>
  <li><a href="https://supabase.com/">Supabase</a> - <a href="https://supabase.com/pricing">pricing</a></li>
  <li><a href="https://www.cloudflare.com/">Cloudflare</a></li>
  <li><a href="https://developers.cloudflare.com/workers/platform/limits/#worker-limits">Cloudflare Workers - limits</a></li>
  <li><a href="https://www.cloudflare.com/products/r2/">Cloudflare R2</a></li>
  <li><a href="https://www.cloudflare.com/products/cdn/">Cloudflare CDN</a></li>
  <li><a href="https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/">Cloudflare Tunnel</a></li>
  <li><a href="https://www.backblaze.com/cloud-storage">Backblaze B2</a></li>
  <li><a href="https://bunny.net/">Bunny CDN</a></li>
</ul>

<h3 id="monitoring-obserwowalność-i-bezpieczeństwo">Monitoring, obserwowalność i bezpieczeństwo</h3>

<ul>
  <li><a href="https://betterstack.com/">Better Stack</a></li>
  <li><a href="https://sentry.io/">Sentry</a></li>
  <li><a href="https://uptimerobot.com/">UptimeRobot</a></li>
  <li><a href="https://grafana.com/products/cloud/">Grafana Cloud</a></li>
  <li><a href="https://github.com/louislam/uptime-kuma">Uptime Kuma</a></li>
  <li><a href="https://www.netdata.cloud/">Netdata</a></li>
  <li><a href="https://glitchtip.com/">GlitchTip</a></li>
  <li><a href="https://help.ubuntu.com/community/UFW">UFW</a></li>
  <li><a href="https://github.com/fail2ban/fail2ban">fail2ban</a></li>
  <li><a href="https://goauthentik.io/">Authentik</a></li>
  <li><a href="https://www.authelia.com/">Authelia</a></li>
  <li><a href="https://github.com/getsops/sops">SOPS</a></li>
  <li><a href="https://github.com/FiloSottile/age">age</a></li>
  <li><a href="https://www.crowdsec.net/">CrowdSec</a></li>
  <li><a href="https://www.1password.dev/">1Password Developer</a></li>
</ul>

<h3 id="technologie-i-narzędzia">Technologie i narzędzia</h3>

<ul>
  <li><a href="https://www.postgresql.org/">PostgreSQL</a></li>
  <li><a href="https://www.sqlite.org/">SQLite</a></li>
  <li><a href="https://litestream.io/">Litestream</a></li>
  <li><a href="https://www.graalvm.org/">GraalVM</a></li>
  <li><a href="https://www.docker.com/">Docker</a></li>
  <li><a href="https://docs.docker.com/compose/">Docker Compose</a></li>
  <li><a href="https://caddyserver.com/">Caddy</a></li>
  <li><a href="https://nginx.org/">nginx</a></li>
  <li><a href="https://www.raspberrypi.com/products/raspberry-pi-5/">Raspberry Pi 5</a></li>
  <li><a href="https://www.kernel.org/">Linux kernel</a></li>
  <li><a href="https://coolify.io/">Coolify</a></li>
  <li><a href="https://casaos.zimaspace.com/">CasaOS</a></li>
  <li><a href="https://aws.amazon.com/s3/">Amazon S3</a></li>
  <li><a href="https://certbot.eff.org/">Certbot</a></li>
  <li><a href="https://restic.net/">restic</a></li>
  <li><a href="https://kubernetes.io/">Kubernetes</a></li>
  <li><a href="https://redis.io/">Redis</a></li>
  <li><a href="https://n8n.io/">n8n</a></li>
</ul>

<h3 id="sieć-i-dostęp">Sieć i dostęp</h3>

<ul>
  <li><a href="https://tailscale.com/">Tailscale</a></li>
</ul>

<h3 id="usługi-i-produkty">Usługi i produkty</h3>

<ul>
  <li><a href="https://workspace.google.com/">Google Workspace</a></li>
  <li><a href="https://www.spotify.com/duo/">Spotify Duo</a></li>
  <li><a href="https://www.youtube.com/premium">YouTube Premium</a></li>
  <li><a href="https://www.jetbrains.com/idea/">JetBrains IntelliJ IDEA</a></li>
  <li><a href="https://www.revolut.com/">Revolut</a></li>
  <li><a href="https://www.zen.com/">ZEN</a></li>
  <li><a href="https://wise.com/">Wise</a></li>
  <li><a href="https://temp-mail.org/">Temp Mail</a></li>
  <li><a href="https://juicysms.com/">JuicySMS</a></li>
  <li><a href="https://support.apple.com/en-us/105078">iCloud Hide My Email</a></li>
</ul>

<h3 id="materiały-inspiracje-i-incydenty">Materiały, inspiracje i incydenty</h3>

<ul>
  <li><a href="https://patoarchitekci.io/155/">Podcast PatoArchitekci.io - #155 Bieda-hosting, czyli miejsce na MVP, Side Projects i inne nasze zabawki</a></li>
  <li><a href="https://www.youtube.com/watch?v=dYzfArtf7qU">Jakub Mrugalski - A gdyby tak zrobić startup w weekend?</a></li>
  <li><a href="https://about.gitlab.com/blog/postmortem-of-database-outage-of-january-31/">GitLab.com outage 2017 - postmortem</a></li>
  <li><a href="https://corporate.ovhcloud.com/en/newsroom/news/informations-site-strasbourg/">Pożar OVHcloud Strasbourg 2021</a></li>
  <li><a href="https://www.lennysnewsletter.com/p/productpass">Lenny’s Product Pass</a></li>
  <li><a href="https://www.patreon.com/promptowy">promptowy</a></li>
  <li><a href="https://oliwier1pl.netlify.app/oferta">oliwier1pl</a></li>
</ul>

<h2 id="michał-mazur">Michał Mazur</h2>

<p><strong>LinkedIn:</strong> <a href="https://www.linkedin.com/in/michmzr/">https://www.linkedin.com/in/michmzr/</a>
<strong>Blogi:</strong> <a href="https://www.geekowojazer.pl/">https://www.geekowojazer.pl/</a> i <a href="https://cybershu.eu/">https://cybershu.eu/</a>
<strong>Kluby Junto:</strong> <a href="https://klubjunto.pl/">Warszawski Klub Junto</a>
<strong>Link.tree:</strong> <a href="https://linktr.ee/michal.mazur?utm_source=linktree_profile_share&amp;ltsid=c6a4db58-0b34-4496-9f89-191e657becc3">https://linktr.ee/michal.mazur</a></p>]]></content><author><name>Michał Mazur</name></author><category term="devops" /><category term="cloud" /><category term="home-lab" /><category term="presentation" /><category term="side-project" /><category term="ai" /><summary type="html"><![CDATA[Na stronie znajdziesz moja prezentacje z konferencji “Javeloper 2026” jak i myślę przydatne linki uzupełniające. Prezentacja PDF: prezentacja Jakie są Twoje wrazenia z prezentacji? Linki Społeczności r/homelab - główna społeczność homelabbers r/VPS r/sysadmin Osoby i profile Pieter Levels na X Homelab i self-hosting The Homelabber selfh.st - Self-hosted content and software Awesome Self-Hosted Mikr.us Hetzner Cloud Tanie VPS-y i darmowe limity VPS Price Tracker: Cheap VPS Lists &amp; Prices freetier.co freetiers.com Free for Developers Chmury i platformy Azure - darmowe limity Google Cloud Free Program Google Cloud Run Oracle Cloud Always Free IBM Cloud Free Tier Vercel - pricing Netlify - pricing and plans Railway Supabase - pricing Cloudflare Cloudflare Workers - limits Cloudflare R2 Cloudflare CDN Cloudflare Tunnel Backblaze B2 Bunny CDN Monitoring, obserwowalność i bezpieczeństwo Better Stack Sentry UptimeRobot Grafana Cloud Uptime Kuma Netdata GlitchTip UFW fail2ban Authentik Authelia SOPS age CrowdSec 1Password Developer Technologie i narzędzia PostgreSQL SQLite Litestream GraalVM Docker Docker Compose Caddy nginx Raspberry Pi 5 Linux kernel Coolify CasaOS Amazon S3 Certbot restic Kubernetes Redis n8n Sieć i dostęp Tailscale Usługi i produkty Google Workspace Spotify Duo YouTube Premium JetBrains IntelliJ IDEA Revolut ZEN Wise Temp Mail JuicySMS iCloud Hide My Email Materiały, inspiracje i incydenty Podcast PatoArchitekci.io - #155 Bieda-hosting, czyli miejsce na MVP, Side Projects i inne nasze zabawki Jakub Mrugalski - A gdyby tak zrobić startup w weekend? GitLab.com outage 2017 - postmortem Pożar OVHcloud Strasbourg 2021 Lenny’s Product Pass promptowy oliwier1pl Michał Mazur LinkedIn: https://www.linkedin.com/in/michmzr/ Blogi: https://www.geekowojazer.pl/ i https://cybershu.eu/ Kluby Junto: Warszawski Klub Junto Link.tree: https://linktr.ee/michal.mazur]]></summary></entry><entry xml:lang="en"><title type="html">Why I Refuse to Ditch Line-by-Line Code Reviews for AI</title><link href="https://www.cybershu.eu/ai-code-review.html" rel="alternate" type="text/html" title="Why I Refuse to Ditch Line-by-Line Code Reviews for AI" /><published>2026-03-04T00:00:00+01:00</published><updated>2026-03-04T00:00:00+01:00</updated><id>https://www.cybershu.eu/ai-code-review</id><content type="html" xml:base="https://www.cybershu.eu/ai-code-review.html"><![CDATA[<p>In the rush to automate everything, traditional code reviews are becoming an endangered species. Why spend hours reading code when an LLM can scan it in seconds? It sounds tempting, but relying solely on AI for reviews might be a costly mistake.</p>

<p>I wouldn’t walk away from developer-led, line-by-line code review, because:</p>

<ol>
  <li>
    <p>For a review to be actually good, it needs multiple perspectives: technical correctness, adherence to coding standards, business correctness (logic), and architecture. I doubt a single prompt can cover all of that.</p>
  </li>
  <li>
    <p>To do a proper review, an AI agent would have to analyze dependencies, understand requirements, build code/logic diagrams, and <em>then</em> review—otherwise you’d need to dump half the repo into context and it’ll blow up in your face. As the saying goes: “It’s a recipe for disaster.”</p>
  </li>
</ol>

<!--more-->

<p>I see AI review as support—maybe even a replacement for small changes—but it won’t replace the eyes of an experienced developer who knows the domain and the system.</p>

<p>The bigger problem I see is organizational culture. AI spits out code in large volumes, and (generally) devs won’t thoroughly review PRs. And we, as developers, will be—are—pushed harder and harder to produce more and more code, faster, using AI. Today we’re told to use one agent; soon we’ll be expected to run at least five in parallel.</p>]]></content><author><name>Michał Mazur</name></author><category term="ai" /><category term="code-review" /><category term="coding" /><category term="software-engineering" /><summary type="html"><![CDATA[In the rush to automate everything, traditional code reviews are becoming an endangered species. Why spend hours reading code when an LLM can scan it in seconds? It sounds tempting, but relying solely on AI for reviews might be a costly mistake. I wouldn’t walk away from developer-led, line-by-line code review, because: For a review to be actually good, it needs multiple perspectives: technical correctness, adherence to coding standards, business correctness (logic), and architecture. I doubt a single prompt can cover all of that. To do a proper review, an AI agent would have to analyze dependencies, understand requirements, build code/logic diagrams, and then review—otherwise you’d need to dump half the repo into context and it’ll blow up in your face. As the saying goes: “It’s a recipe for disaster.”]]></summary></entry><entry xml:lang="en"><title type="html">Vibe coding might be very hard - presentation</title><link href="https://www.cybershu.eu/vibe-coding-might-be-very-hard.html" rel="alternate" type="text/html" title="Vibe coding might be very hard - presentation" /><published>2025-06-15T00:00:00+02:00</published><updated>2025-06-15T00:00:00+02:00</updated><id>https://www.cybershu.eu/vibe-coding</id><content type="html" xml:base="https://www.cybershu.eu/vibe-coding-might-be-very-hard.html"><![CDATA[<p>Building apps with AI is easier than ever and ever.</p>

<p>Doing it right? Still hard. It still requires knowledge and skills. But it’s easier than ever.</p>

<!--more-->
<p>I put together a few practical tips for builders of new era.</p>

<h2 id="presentation">Presentation</h2>
<embed src="../assets/docs/li-vibe-coding.pdf" type="application/pdf" width="100%" height="600px" />

<p><a href="../assets/docs/li-vibe-coding.pdf">Download PDF</a></p>

<h2 id="fast-tips-for-you">Fast tips for you</h2>
<h3 id="security">Security</h3>

<ul>
  <li><strong>Keep API keys secret:</strong> Never hardcode keys in your app. Store secrets in environment variables or a free secrets manager to keep them safe. 1Password has some interesting features that are worth looking at!</li>
  <li><strong>Scan for vulnerabilities:</strong> Run a free Snyk scan (or enable GitHub Dependabot) to catch known security flaws in your code/dependencies early.</li>
  <li><strong>HTTPS everywhere:</strong> Always serve your app over HTTPS. Tools like Let’s Encrypt give free SSL certificates!</li>
  <li><strong>Lock your accounts:</strong> Enable 2-factor authentication on critical services (GitHub, cloud platforms, AI tools etc.). It’s free and prevents easy hacks on your accounts.</li>
  <li><strong>Set hard budget limits</strong> in all tools that charge for usage, like Chat-GPT. It might save you a lot of money!</li>
</ul>

<h3 id="version-control">Version Control</h3>
<ul>
  <li><strong>Stop emailing zip files:</strong> Use Git for version control. It tracks every change so you can undo mistakes and never lose code again.</li>
  <li><strong>Free cloud backup:</strong> Push your code to GitHub or GitLab (free private repos) for safekeeping. If your laptop crashes, your code stays safe in the cloud.</li>
  <li><strong>Branch for safety:</strong> Experiment on a new branch instead of directly on main. Merge when it’s working, or delete it if not — no harm done to your main codebase.</li>
  <li><strong>GitHub Desktop helps:</strong> If the command line scares you, use the free GitHub Desktop app. It’s a point-and-click way to commit, push, and pull code – no terminal needed. Most coding editors have already build-in graphic interface for GIT.</li>
  <li><strong>Automate testing &amp; deploys:</strong> Use GitHub Actions (free) to run checks or deploy your app on every push. Automation catches issues early and saves manual effort.</li>
</ul>

<h3 id="deployment">Deployment</h3>
<ul>
  <li><strong>One-click hosting:</strong> Deploy your app on platforms like Railway Vercel. You just push code and chill.</li>
  <li><strong>No server? No problem:</strong> Firebase offers hosting, database, and auth with a free plan. Great way to get backend features without managing any servers.</li>
  <li><strong>Static site hero:</strong> Use Netlify, Vercel, or GitHub Pages for a static website or frontend app. They’re free – just connect your repo or drop your files to go live.</li>
  <li><strong>Continuous deploy:</strong> Link your code repo to your host (Railway, Vercel, etc.). Every time you push changes, the service auto-deploys your app. No more manual uploads!</li>
  <li><strong>Serverless functions:</strong> Need a bit of backend logic? Use cloud functions on a free tier (Firebase Functions, Vercel,  Netlify, AWS, GCP, Azure). Your code runs on demand.</li>
</ul>

<h3 id="rules">Rules</h3>
<ul>
  <li><strong>Split project into smaller tasks</strong> – Easier to manage, debug, and parallelize.</li>
  <li><strong>Write detailed documentation first</strong> – Force model/agent to follow it. Include goals, edge cases, and examples.</li>
  <li><strong>Monitor token and compute costs</strong> – Large files or loops can explode usage. If your app is using AI, then integrate your app with LangSmith or a similar observability tool.</li>
  <li><strong>Limit workspace scope</strong> – Restrict to relevant directories or files only for each task.</li>
  <li><strong>Define clear input/output format</strong> – Avoid ambiguity. Keep structure strict and predictable. Define all details and rules in each prompt.</li>
  <li><strong>Use isolated test cases</strong> – Help the model validate logic step-by-step. Ask the model to create tests according to BDD (Behavior Driven Development).</li>
  <li><strong>Fail fast, retry smart</strong> – Catch issues early, and design fallback or retry logic.</li>
</ul>

<h2 id="wrap-up">Wrap-up</h2>
<p>I hope the article at least intrigued you, you learned something new. If you would like to see more of such articles, please let me know!</p>

<h2 id="contact">Contact</h2>
<p>Please leave any comments to let me know. If you have any questions, please feel free to contact me directly on:</p>
<ul>
  <li>Twitter: <a href="https://twitter.com/MichalMzr">https://twitter.com/MichalMzr</a></li>
  <li>LinkedIn: <a href="https://www.linkedin.com/in/michmzr/">https://www.linkedin.com/in/michmzr/</a></li>
</ul>

<p>You can also find my posts on my second blog <a href="https://www.geekowojazer.pl/">Geekowojażer.pl</a></p>]]></content><author><name>Michał Mazur</name></author><category term="vibe-coding" /><category term="coding" /><category term="cursor" /><category term="llm" /><category term="ai" /><category term="devops" /><category term="security" /><category term="practises" /><category term="presentation" /><category term="cloud" /><category term="tools" /><summary type="html"><![CDATA[Building apps with AI is easier than ever and ever. Doing it right? Still hard. It still requires knowledge and skills. But it’s easier than ever.]]></summary></entry><entry xml:lang="en"><title type="html">No More PowerPoint - Use Marp</title><link href="https://www.cybershu.eu/no-more-powerpoint.html" rel="alternate" type="text/html" title="No More PowerPoint - Use Marp" /><published>2025-02-19T00:00:00+01:00</published><updated>2025-02-19T00:00:00+01:00</updated><id>https://www.cybershu.eu/no-more-powerpoint</id><content type="html" xml:base="https://www.cybershu.eu/no-more-powerpoint.html"><![CDATA[<p>Last week, I was tasked with giving a presentation on “Dependabot” - a tool for automatically updating dependencies in projects.</p>

<p>After the last presentations, I felt tired of PowerPoint and decided to do something different.
<!--more--></p>

<h2 id="why-i-got-tired-of-powerpoint">Why I got tired of PowerPoint</h2>

<p>Form, form, form. I was tired of the form.
PowerPoint forces to focus on form, layouts, colors, and pixels etc.
I don’t like frontend development, I’m not a designer.
I prefer to focus on the content, not the layout, pixels.
I a presentation that was simple and minimalistic.</p>

<p><img src="../assets/images/posts/no-more-powerpoint/meme-the-office.jpg" alt="The Office meme" class="image image--md" style="display: block; margin-left: auto; margin-right: auto;" /></p>

<h2 id="marp---a-simple-solution">Marp - a simple solution</h2>

<p>I used Markdown - a text format for creating presentations. Without using any AI :P</p>

<p>It’s simple because all you need is a Markdown text file and a tool to display such a file, such as the <code class="language-plaintext highlighter-rouge">Marp for VS Code</code> plugin.
Then you can export the presentation to HTML, PDF. IF you don’t use visual studio code, then you can use for example CLI to generate a presentation in PDF/HTML format.</p>

<p>The simplicity of this solution convinced me.</p>

<h2 id="example-of-marp-presentation">Example of Marp presentation</h2>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>
<span class="na">marp</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">theme</span><span class="pi">:</span> <span class="s">default</span>
<span class="na">paginate</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">backgroundColor</span><span class="pi">:</span> <span class="c1">#fff</span>
<span class="na">style</span><span class="pi">:</span> <span class="pi">|</span>
  <span class="s">.columns {</span>
    <span class="s">display: grid;</span>
    <span class="s">grid-template-columns: repeat(2, minmax(0, 1fr));</span>
    <span class="s">gap: 1rem;</span>
  <span class="s">}</span>
  <span class="s">.small-text {</span>
    <span class="s">font-size: 0.75em;</span>
  <span class="s">}</span>
  <span class="s">.center {</span>
    <span class="s">text-align: center;</span>
  <span class="s">}</span>

  <span class="s">header,</span>
  <span class="s">footer {</span>
    <span class="s">padding:20px</span>
  <span class="s">}</span>

  <span class="s">img {</span>
    <span class="s">max-width: 100%;</span>
    <span class="s">max-height: 100%;</span>
  <span class="s">}</span>
<span class="s">---</span>

<span class="gh"># :popcorn:Dependabot: Automated Dependency Updates</span>
<span class="p">![</span><span class="nv">bg right</span><span class="p">](</span><span class="sx">./dp_logo.png</span><span class="p">)</span>

<span class="nt">&lt;small&gt;</span>
Michał Mazur
<span class="nt">&lt;/small&gt;</span>
<span class="p">
---</span>
<span class="c">&lt;!-- headingDivider: 0--&gt;</span><span class="sb">


</span><span class="gh"># What is Dependabot? 🤖</span>

Imagine having a dedicated team member who:
<span class="p">
-</span> Never sleeps and its fully automated
<span class="p">-</span> Checks your dependencies 24/7
<span class="p">-</span> Creates perfect pull requests
<span class="p">-</span> Knows about security issues before you do
<span class="p">-</span> Supports multiple ecosystems:
<span class="p">  -</span> github actions, npm, pip, Maven, NuGet, Bundler...
<span class="p">
---
</span>
<span class="gh"># Short Live Demo Time! 🎬</span>
</code></pre></div></div>

<p>In a result you can get sth like that in PDF format:
<a href="../assets/images/posts/no-more-powerpoint/marp_example.pdf">pdf</a></p>

<h2 id="links">Links</h2>
<ul>
  <li><strong>Marpit:</strong> <a href="https://marpit.marp.app/">https://marpit.marp.app/</a></li>
  <li><strong>Marp for VS Code:</strong> <a href="https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode">https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode</a></li>
</ul>

<h2 id="wrap-up">Wrap-up</h2>
<p>I hope the article at least intrigued you, you learned something new.</p>

<h2 id="contact">Contact</h2>
<p>Please leave any comments to let me know. If you have any questions, please feel free to contact me directly on:</p>
<ul>
  <li>Twitter: <a href="https://twitter.com/MichalMzr">https://twitter.com/MichalMzr</a></li>
  <li>LinkedIn: <a href="https://www.linkedin.com/in/michmzr/">https://www.linkedin.com/in/michmzr/</a></li>
</ul>

<p>You can also find my posts on my second blog <a href="https://www.geekowojazer.pl/">Geekowojażer.pl</a></p>]]></content><author><name>Michał Mazur</name></author><category term="marp" /><category term="power-point" /><category term="presenation" /><category term="visual" /><category term="tool" /><category term="markdown" /><category term="pitch" /><category term="minimalism" /><category term="common-mark" /><summary type="html"><![CDATA[Last week, I was tasked with giving a presentation on “Dependabot” - a tool for automatically updating dependencies in projects. After the last presentations, I felt tired of PowerPoint and decided to do something different.]]></summary></entry><entry xml:lang="en"><title type="html">How to create new docker image tag without push on JFrog and AWS ECR</title><link href="https://www.cybershu.eu/new-tag-without-push-jfrog-aws-ecr.html" rel="alternate" type="text/html" title="How to create new docker image tag without push on JFrog and AWS ECR" /><published>2024-12-20T00:00:00+01:00</published><updated>2024-12-20T00:00:00+01:00</updated><id>https://www.cybershu.eu/new-tag-without-push</id><content type="html" xml:base="https://www.cybershu.eu/new-tag-without-push-jfrog-aws-ecr.html"><![CDATA[<p>Hi  ✋,</p>

<p>I was recently working on signing Docker images and found a performance issue. In order to sign the image,
I had to pull the image from the registry, calculate the hash and create the signature file, and push the new tag to artifactory. This process took a lot of time, especially if the image was large. I was looking for a way to optimise this process.
 <!--more-->
I found a hack to remotely create new tags on JFrog and AWS ECR docker repositories without pulling and pushing the image. It’s a simple trick, but it can save you a lot of time. I will show you an example of how to do it on Github Actions</p>

<h2 id="classic-method">Classic method</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker pull artifactory.example.com/my-image:1.0.0
docker tag artifactory.example.com/my-image:1.0.0 artifactory.example.com/my-image:1.0.1
docker push artifactory.example.com/my-image:1.0.1
</code></pre></div></div>

<h2 id="jfrog">JFrog</h2>

<p>Below exampl pipeline steps uses JFrog API to fetch the image manifest and add new tag to the image <code class="language-plaintext highlighter-rouge">$</code> tagged <code class="language-plaintext highlighter-rouge">$</code>.</p>

<h2 id="pipeline-configuration">Pipeline configuration</h2>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">env</span><span class="pi">:</span>
  <span class="na">DEVHUB_REGISTRY_URL</span><span class="pi">:</span> <span class="s">artifactory.example.com</span> <span class="c1"># JFrog registry url</span>
  <span class="na">DEVHUB_REPO_NAME</span><span class="pi">:</span> <span class="s">docker</span> <span class="c1"># JFrog docker repository name</span>
  <span class="na">IMAGE_NAME</span><span class="pi">:</span> <span class="s">my-image</span> <span class="c1"># Docker image name</span>
  <span class="na">SEMVER</span><span class="pi">:</span> <span class="s">1.0.0</span> <span class="c1"># Current image tag</span>
  <span class="na">TARGET_SEMVER</span><span class="pi">:</span> <span class="s">1.0.1</span> <span class="c1"># New image tag</span>
</code></pre></div></div>

<p><strong>Secrets:</strong></p>
<ul>
  <li>ARTIFACTORY_USERNAME</li>
  <li>ARTIFACTORY_TOKEN</li>
</ul>

<h3 id="pipeline-steps">Pipeline steps</h3>
<script src="https://gist.github.com/da298ab933908a3d0610e75b18a513d9.js?file=jfrog-steps.yml"> </script>

<h2 id="aws-ecr">AWS ECR</h2>

<h3 id="pipeline-configuration-1">Pipeline configuration</h3>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">env</span><span class="pi">:</span>
  <span class="na">AWS_REGION</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span> <span class="c1"># AWS region name i.e us-east-1</span>
  <span class="na">AWS_WORKFLOW_ROLE</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span> <span class="c1"># AWS role arn</span>
  <span class="na">AWS_ROLE_SESSION_NAME</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span> <span class="c1"># AWS role session name</span>

  <span class="na">IMAGE_NAME</span><span class="pi">:</span> <span class="s">my-image</span> <span class="c1"># Docker image name</span>
  <span class="na">SEMVER</span><span class="pi">:</span> <span class="s">1.0.0</span> <span class="c1"># Current image tag</span>
  <span class="na">TARGET_SEMVER</span><span class="pi">:</span> <span class="s">1.0.1</span> <span class="c1"># New image tag</span>
</code></pre></div></div>

<h3 id="pipeline-steps-1">Pipeline steps</h3>
<p class="warning"><strong>Warning:</strong>
Authentication with AWS ECR requires AWS credentials. Please make sure you have configured AWS credentials in your pipeline. My example uses one of the possible methods to configure AWS credentials in GitHub Actions.</p>

<script src="https://gist.github.com/da298ab933908a3d0610e75b18a513d9.js?file=ecr-steps.yml"> </script>

<h2 id="wrap-up">Wrap-up</h2>
<p>I hope the article at least intrigued you, you learned something new.</p>

<h2 id="contact">Contact</h2>
<p>Please leave any comments to let me know. If you have any questions, please feel free to contact me directly on:</p>
<ul>
  <li>Twitter: <a href="https://twitter.com/MichalMzr">https://twitter.com/MichalMzr</a></li>
  <li>LinkedIn: <a href="https://www.linkedin.com/in/michmzr/">https://www.linkedin.com/in/michmzr/</a></li>
</ul>

<p>You can also find my posts on my second blog <a href="https://www.geekowojazer.pl/">Geekowojażer.pl</a></p>]]></content><author><name>Michał Mazur</name></author><category term="docker" /><category term="jfrog" /><category term="artifactory" /><category term="ecr" /><category term="aws" /><category term="tag" /><category term="push" /><category term="pull" /><category term="image" /><category term="container" /><category term="registry" /><summary type="html"><![CDATA[Hi ✋, I was recently working on signing Docker images and found a performance issue. In order to sign the image, I had to pull the image from the registry, calculate the hash and create the signature file, and push the new tag to artifactory. This process took a lot of time, especially if the image was large. I was looking for a way to optimise this process.]]></summary></entry><entry xml:lang="en"><title type="html">What is a work log and how to use it to speed up your career?</title><link href="https://www.cybershu.eu/work-log-career-10x.html" rel="alternate" type="text/html" title="What is a work log and how to use it to speed up your career?" /><published>2024-10-11T00:00:00+02:00</published><updated>2024-10-11T00:00:00+02:00</updated><id>https://www.cybershu.eu/work-log</id><content type="html" xml:base="https://www.cybershu.eu/work-log-career-10x.html"><![CDATA[<h1 id="what-is-a-work-log-and-how-to-use-it-to-speed-up-your-career">What is a work log and how to use it to speed up your career?</h1>

<p>As I was diving into “The Software Engineer’s Guidebook,” by Gergely Orosz I stumbled upon a game-changing tip: keeping a work log.</p>

<p>This practice has been a revelation for me—it’s more than just tracking daily tasks. A work log helps you:</p>

<p>✅ Track Your Progress: Stay on top of your projects and see how far you’ve come.</p>

<p>🎉 Celebrate Wins: Big or small, every achievement counts. Documenting them keeps your morale high.</p>

<p>🔄 Learn from Mistakes: Reflect on challenges and turn them into growth opportunities.</p>

<p>💼 Prepare for Promotions: When it’s time to discuss your next career move, having a detailed record of your contributions is invaluable.</p>

<p>Want to start your own work log? I’ve put together a simple template to get you going:</p>

<h2 id="my-weekly-template">My weekly template</h2>

<div class="language-md highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">-</span> What work do I feel most proud of?
<span class="p">-</span> Are there themes in these projects I should be thinking about? What’s the big picture of what I’m working on? (am I working a lot on security? localization?).
<span class="p">-</span> What do I wish I was doing more / less of?
<span class="p">-</span> Which of my projects had the effect I wanted, and which didn’t? Why might that have been?
<span class="p">-</span> What could have gone better with project X? What might I want to do differently next time?

<span class="gh"># Developer Work Log</span>

<span class="gu">## Week of [Month Day, Year]</span>

<span class="gu">### Goals</span>
<span class="p">-</span> [ ]
<span class="p">-</span> [ ]
<span class="p">-</span> [ ]<span class="sb">


</span><span class="gu">### Monday, [Date]</span>
<span class="gu">#### Tasks</span>
<span class="p">-</span> [ ]

<span class="gu">#### 🚀 Achievements</span>
<span class="p">-</span>

<span class="gu">#### 🛠️ Challenges</span>
<span class="p">-</span>

<span class="gu">#### 💡 Learnings</span>
<span class="p">-</span>

<span class="gu">#### 🎯 Goals for Tomorrow</span>
<span class="p">-</span><span class="sb">


</span><span class="gu">### Tuesday, [Date]</span>
<span class="gu">#### Tasks</span>
<span class="p">-</span> [ ]

<span class="gu">#### 🚀 Achievements</span>
<span class="p">-</span>

<span class="gu">#### 🛠️ Challenges</span>
<span class="p">-</span>

<span class="gu">#### 💡 Learnings</span>
<span class="p">-</span>

<span class="gu">#### 🎯 Goals for Tomorrow</span>
<span class="p">-</span><span class="sb">



</span><span class="gu">### Wednesday, [Date]</span>
<span class="gu">#### Tasks</span>
<span class="p">-</span> [ ]

<span class="gu">#### 🚀 Achievements</span>
<span class="p">-</span>

<span class="gu">#### 🛠️ Challenges</span>
<span class="p">-</span>

<span class="gu">#### 💡 Learnings</span>
<span class="p">-</span>

<span class="gu">#### 🎯 Goals for Tomorrow</span>
<span class="p">-</span>

<span class="gu">### Thursday,  [Date]</span>
<span class="gu">#### Tasks</span>
<span class="p">-</span> [ ]

<span class="gu">#### 🚀 Achievements</span>
<span class="p">-</span>

<span class="gu">#### 🛠️ Challenges</span>
<span class="p">-</span>

<span class="gu">#### 💡 Learnings</span>
<span class="p">-</span>

<span class="gu">#### 🎯 Goals for Tomorrow</span>
<span class="p">-</span><span class="sb">


</span><span class="gu">### Friday, [Date]</span>
<span class="gu">#### Tasks</span>
<span class="p">-</span> [ ]

<span class="gu">#### 🚀 Achievements</span>
<span class="p">-</span>

<span class="gu">#### 🛠️ Challenges</span>
<span class="p">-</span>

<span class="gu">#### 💡 Learnings</span>
<span class="p">-</span>

<span class="gu">#### 🎯 Goals for Tomorrow</span>
<span class="p">-


---
</span>
<span class="gu">## Weekly Summary</span>

<span class="gu">### 🎯 Weekly Goals</span>
<span class="p">-</span> [Goal 1]
<span class="p">-</span> [Goal 2]
<span class="p">-</span> [Goal 3]

<span class="gu">### 🚀 Achievements</span>
<span class="p">-</span> [Major achievement 1]
<span class="p">-</span> [Major achievement 2]
<span class="p">-</span> [Major achievement 3]

<span class="gu">### 🛠️ Challenges</span>
<span class="p">-</span> [Significant challenge 1]
<span class="p">-</span> [Significant challenge 2]
<span class="p">-</span> [Significant challenge 3]

<span class="gu">### 💡 Learnings</span>
<span class="p">-</span> [Key learning 1]
<span class="p">-</span> [Key learning 2]
<span class="p">-</span> [Key learning 3]

<span class="gu">### 📅 Planning for Next Week</span>
<span class="p">-</span> [Objective 1]
<span class="p">-</span> [Objective 2]
<span class="p">-</span> [Objective 3]
</code></pre></div></div>]]></content><author><name>Michał Mazur</name></author><category term="work-log" /><category term="career" /><category term="development" /><category term="work" /><category term="job" /><summary type="html"><![CDATA[What is a work log and how to use it to speed up your career? As I was diving into “The Software Engineer’s Guidebook,” by Gergely Orosz I stumbled upon a game-changing tip: keeping a work log. This practice has been a revelation for me—it’s more than just tracking daily tasks. A work log helps you: ✅ Track Your Progress: Stay on top of your projects and see how far you’ve come. 🎉 Celebrate Wins: Big or small, every achievement counts. Documenting them keeps your morale high. 🔄 Learn from Mistakes: Reflect on challenges and turn them into growth opportunities. 💼 Prepare for Promotions: When it’s time to discuss your next career move, having a detailed record of your contributions is invaluable. Want to start your own work log? I’ve put together a simple template to get you going: My weekly template - What work do I feel most proud of? - Are there themes in these projects I should be thinking about? What’s the big picture of what I’m working on? (am I working a lot on security? localization?). - What do I wish I was doing more / less of? - Which of my projects had the effect I wanted, and which didn’t? Why might that have been? - What could have gone better with project X? What might I want to do differently next time? # Developer Work Log ## Week of [Month Day, Year] ### Goals - [ ] - [ ] - [ ] ### Monday, [Date] #### Tasks - [ ] #### 🚀 Achievements - #### 🛠️ Challenges - #### 💡 Learnings - #### 🎯 Goals for Tomorrow - ### Tuesday, [Date] #### Tasks - [ ] #### 🚀 Achievements - #### 🛠️ Challenges - #### 💡 Learnings - #### 🎯 Goals for Tomorrow - ### Wednesday, [Date] #### Tasks - [ ] #### 🚀 Achievements - #### 🛠️ Challenges - #### 💡 Learnings - #### 🎯 Goals for Tomorrow - ### Thursday, [Date] #### Tasks - [ ] #### 🚀 Achievements - #### 🛠️ Challenges - #### 💡 Learnings - #### 🎯 Goals for Tomorrow - ### Friday, [Date] #### Tasks - [ ] #### 🚀 Achievements - #### 🛠️ Challenges - #### 💡 Learnings - #### 🎯 Goals for Tomorrow - --- ## Weekly Summary ### 🎯 Weekly Goals - [Goal 1] - [Goal 2] - [Goal 3] ### 🚀 Achievements - [Major achievement 1] - [Major achievement 2] - [Major achievement 3] ### 🛠️ Challenges - [Significant challenge 1] - [Significant challenge 2] - [Significant challenge 3] ### 💡 Learnings - [Key learning 1] - [Key learning 2] - [Key learning 3] ### 📅 Planning for Next Week - [Objective 1] - [Objective 2] - [Objective 3]]]></summary></entry><entry xml:lang="en"><title type="html">Building and Running a JAR from the Tests Directory</title><link href="https://www.cybershu.eu/articles/mvn-tests-build-run-jar.html" rel="alternate" type="text/html" title="Building and Running a JAR from the Tests Directory" /><published>2024-07-01T00:00:00+02:00</published><updated>2024-07-01T00:00:00+02:00</updated><id>https://www.cybershu.eu/articles/build-run-jar-from-tests</id><content type="html" xml:base="https://www.cybershu.eu/articles/mvn-tests-build-run-jar.html"><![CDATA[<p>Hello there ✋,</p>

<p>Hello ✋, I was tasked with integrating a Java class for data generation into our
CI/CD pipeline. The goal was to create a shaded JAR that could run directly from
the pipeline.</p>

<p>The challenge was that the class was in the tests directory, which Maven doesn’t
support for building JARs. I resolved this by using the maven-shade-plugin and
maven-resources-plugin to build a shaded JAR, including necessary resources like
SQL schema files.</p>

<p><img src="../assets/images/posts/mvn-tests-jar/whereisfile.png" alt="" /></p>

<p>I hope this example saves you time during development! 🚀</p>

<h2 id="code">Code</h2>

<h3 id="maven">Maven</h3>

<p>This Maven configuration uses two key plugins to build and run a JAR file with
an executable Java class (MainTest) in the tests directory: the Maven Shade
Plugin and the Maven Resources Plugin.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="nt">&lt;project</span> <span class="na">xmlns=</span><span class="s">"http://maven.apache.org/POM/4.0.0"</span>
         <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
         <span class="na">xsi:schemaLocation=</span><span class="s">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;modelVersion&gt;</span>4.0.0<span class="nt">&lt;/modelVersion&gt;</span>

  <span class="nt">&lt;groupId&gt;</span>eu.cybershu<span class="nt">&lt;/groupId&gt;</span>
  <span class="nt">&lt;artifactId&gt;</span>mvn-test-jar<span class="nt">&lt;/artifactId&gt;</span>
  <span class="nt">&lt;version&gt;</span>1.0-SNAPSHOT<span class="nt">&lt;/version&gt;</span>

  <span class="nt">&lt;properties&gt;</span>
    <span class="nt">&lt;maven.compiler.source&gt;</span>17<span class="nt">&lt;/maven.compiler.source&gt;</span>
    <span class="nt">&lt;maven.compiler.target&gt;</span>17<span class="nt">&lt;/maven.compiler.target&gt;</span>
    <span class="nt">&lt;project.build.sourceEncoding&gt;</span>UTF-8<span class="nt">&lt;/project.build.sourceEncoding&gt;</span>
  <span class="nt">&lt;/properties&gt;</span>

  <span class="nt">&lt;profiles&gt;</span>
    <span class="nt">&lt;profile&gt;</span>
      <span class="nt">&lt;id&gt;</span>test-jar<span class="nt">&lt;/id&gt;</span>
      <span class="nt">&lt;build&gt;</span>
        <span class="nt">&lt;plugins&gt;</span>
          <span class="nt">&lt;plugin&gt;</span>
            <span class="nt">&lt;groupId&gt;</span>org.apache.maven.plugins<span class="nt">&lt;/groupId&gt;</span>
            <span class="nt">&lt;artifactId&gt;</span>maven-shade-plugin<span class="nt">&lt;/artifactId&gt;</span>
            <span class="nt">&lt;version&gt;</span>3.6.0<span class="nt">&lt;/version&gt;</span>
            <span class="nt">&lt;executions&gt;</span>
              <span class="nt">&lt;execution&gt;</span>
                <span class="nt">&lt;goals&gt;</span>
                  <span class="nt">&lt;goal&gt;</span>shade<span class="nt">&lt;/goal&gt;</span>
                <span class="nt">&lt;/goals&gt;</span>
                <span class="nt">&lt;configuration&gt;</span>
                  <span class="nt">&lt;shadedArtifactAttached&gt;</span>true<span class="nt">&lt;/shadedArtifactAttached&gt;</span>
                  <span class="nt">&lt;transformers&gt;</span>
                    <span class="nt">&lt;transformer</span> <span class="na">implementation=</span>
                                   <span class="s">"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"</span><span class="nt">&gt;</span>
                      <span class="nt">&lt;mainClass&gt;</span>eu.cybershu.MainTest<span class="nt">&lt;/mainClass&gt;</span>
                    <span class="nt">&lt;/transformer&gt;</span>
                  <span class="nt">&lt;/transformers&gt;</span>
                <span class="nt">&lt;/configuration&gt;</span>
              <span class="nt">&lt;/execution&gt;</span>
            <span class="nt">&lt;/executions&gt;</span>
          <span class="nt">&lt;/plugin&gt;</span>

          <span class="nt">&lt;plugin&gt;</span>
            <span class="nt">&lt;groupId&gt;</span>org.apache.maven.plugins<span class="nt">&lt;/groupId&gt;</span>
            <span class="nt">&lt;artifactId&gt;</span>maven-resources-plugin<span class="nt">&lt;/artifactId&gt;</span>
            <span class="nt">&lt;version&gt;</span>3.3.1<span class="nt">&lt;/version&gt;</span>
            <span class="nt">&lt;executions&gt;</span>
              <span class="nt">&lt;execution&gt;</span>
                <span class="nt">&lt;id&gt;</span>copy-test-classes<span class="nt">&lt;/id&gt;</span>
                <span class="nt">&lt;phase&gt;</span>process-test-classes<span class="nt">&lt;/phase&gt;</span>
                <span class="nt">&lt;goals&gt;</span>
                  <span class="nt">&lt;goal&gt;</span>copy-resources<span class="nt">&lt;/goal&gt;</span>
                <span class="nt">&lt;/goals&gt;</span>
                <span class="nt">&lt;configuration&gt;</span>
                  <span class="nt">&lt;outputDirectory&gt;</span>${project.build.outputDirectory}
                  <span class="nt">&lt;/outputDirectory&gt;</span>
                  <span class="nt">&lt;resources&gt;</span>
                    <span class="nt">&lt;resource&gt;</span>
                      <span class="nt">&lt;directory&gt;</span>${project.build.testOutputDirectory}
                      <span class="nt">&lt;/directory&gt;</span>
                      <span class="nt">&lt;includes&gt;</span>
                        <span class="nt">&lt;include&gt;</span>**/*.class<span class="nt">&lt;/include&gt;</span>
                      <span class="nt">&lt;/includes&gt;</span>
                    <span class="nt">&lt;/resource&gt;</span>
                  <span class="nt">&lt;/resources&gt;</span>
                <span class="nt">&lt;/configuration&gt;</span>
              <span class="nt">&lt;/execution&gt;</span>
            <span class="nt">&lt;/executions&gt;</span>
          <span class="nt">&lt;/plugin&gt;</span>
        <span class="nt">&lt;/plugins&gt;</span>
      <span class="nt">&lt;/build&gt;</span>
    <span class="nt">&lt;/profile&gt;</span>
  <span class="nt">&lt;/profiles&gt;</span>
<span class="nt">&lt;/project&gt;</span>
</code></pre></div></div>

<h4 id="maven-shade-plugin">Maven Shade Plugin</h4>

<p>The Maven Shade Plugin is used to package the project into an executable JAR
file. This plugin performs several tasks, such as combining dependencies into a
single JAR and modifying the manifest file to specify the main class.</p>

<p>Shading phase packs the project into a JAR file that includes dependencies.</p>

<p><strong>Configuration:</strong></p>

<ul>
  <li>shadedArtifactAttached: When set to true, this ensures the shaded JAR is
attached as an additional artifact.</li>
  <li>transformers: This section modifies the JAR manifest. The
ManifestResourceTransformer is used to specify the main class (
eu.cybershu.MainTest).</li>
</ul>

<h4 id="maven-resources-plugin">Maven Resources Plugin</h4>

<p>The Maven Resources Plugin is employed here to ensure that the compiled test
classes are included in the final build output. This is necessary because the
main class (MainTest) resides in the <code class="language-plaintext highlighter-rouge">tests</code> directory.</p>

<p><strong>Key Elements:</strong></p>

<ul>
  <li>Execution ID (<code class="language-plaintext highlighter-rouge">copy-test-classes</code>): Identifies this specific execution of the
plugin.</li>
  <li>Phase (<code class="language-plaintext highlighter-rouge">process-test-classes</code>): Specifies when this plugin should run during
the build lifecycle.</li>
</ul>

<p><strong>Configuration:</strong></p>

<ul>
  <li>outputDirectory: Specifies where the resources should be copied to, typically
the main output directory of the build.</li>
  <li>resources: Defines which resources to copy. Here, it includes all .class files
from the test output directory.</li>
</ul>

<p>By configuring this plugin, the build process ensures that the compiled test
classes (including <code class="language-plaintext highlighter-rouge">MainTest.class</code>) are copied to the main output directory.
This step is crucial for including the MainTest class in the final shaded JAR
file.</p>

<h3 id="java">Java</h3>

<p><strong>MainTest.java - Executable Class</strong></p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MainTest</span> <span class="o">{</span>
  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"Let's print a file......"</span><span class="o">);</span>

    <span class="nc">BufferedReader</span> <span class="n">reader</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BufferedReader</span><span class="o">(</span><span class="k">new</span> <span class="nc">InputStreamReader</span><span class="o">(</span>
      <span class="nc">MainTest</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getClassLoader</span><span class="o">().</span><span class="na">getResourceAsStream</span><span class="o">(</span><span class="s">"any_file.txt"</span><span class="o">)));</span>
    <span class="n">reader</span><span class="o">.</span><span class="na">lines</span><span class="o">().</span><span class="na">forEach</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">::</span><span class="n">println</span><span class="o">);</span>
  <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="build-and-running">Build and running</h2>

<p><strong>How to build:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mvn package <span class="nt">-P</span> test-jar
</code></pre></div></div>

<p><strong>How to run:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java <span class="nt">-jar</span> ./target/mvn-test-jar-1.0-SNAPSHOT-shaded.jar
</code></pre></div></div>

<h2 id="contact">Contact</h2>

<p>I hope you find this useful! 😊 Please leave comments or contact me directly if
you have any questions:</p>

<ul>
  <li>Twitter: <a href="https://twitter.com/MichalMzr">https://twitter.com/MichalMzr</a></li>
  <li>LinkedIn: <a href="https://www.linkedin.com/in/michmzr/">https://www.linkedin.com/in/michmzr/</a></li>
</ul>

<p>Check out more of my posts on my second
blog: <a href="https://www.geekowojazer.pl/">Geekowojażer.pl</a></p>]]></content><author><name>Michał Mazur</name></author><category term="java" /><category term="maven" /><category term="jar" /><category term="jvm" /><category term="build" /><category term="testing" /><summary type="html"><![CDATA[Hello there ✋, Hello ✋, I was tasked with integrating a Java class for data generation into our CI/CD pipeline. The goal was to create a shaded JAR that could run directly from the pipeline. The challenge was that the class was in the tests directory, which Maven doesn’t support for building JARs. I resolved this by using the maven-shade-plugin and maven-resources-plugin to build a shaded JAR, including necessary resources like SQL schema files. I hope this example saves you time during development! 🚀 Code Maven This Maven configuration uses two key plugins to build and run a JAR file with an executable Java class (MainTest) in the tests directory: the Maven Shade Plugin and the Maven Resources Plugin. &lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt; &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt; &lt;groupId&gt;eu.cybershu&lt;/groupId&gt; &lt;artifactId&gt;mvn-test-jar&lt;/artifactId&gt; &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt; &lt;properties&gt; &lt;maven.compiler.source&gt;17&lt;/maven.compiler.source&gt; &lt;maven.compiler.target&gt;17&lt;/maven.compiler.target&gt; &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt; &lt;/properties&gt; &lt;profiles&gt; &lt;profile&gt; &lt;id&gt;test-jar&lt;/id&gt; &lt;build&gt; &lt;plugins&gt; &lt;plugin&gt; &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt; &lt;artifactId&gt;maven-shade-plugin&lt;/artifactId&gt; &lt;version&gt;3.6.0&lt;/version&gt; &lt;executions&gt; &lt;execution&gt; &lt;goals&gt; &lt;goal&gt;shade&lt;/goal&gt; &lt;/goals&gt; &lt;configuration&gt; &lt;shadedArtifactAttached&gt;true&lt;/shadedArtifactAttached&gt; &lt;transformers&gt; &lt;transformer implementation= "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"&gt; &lt;mainClass&gt;eu.cybershu.MainTest&lt;/mainClass&gt; &lt;/transformer&gt; &lt;/transformers&gt; &lt;/configuration&gt; &lt;/execution&gt; &lt;/executions&gt; &lt;/plugin&gt; &lt;plugin&gt; &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt; &lt;artifactId&gt;maven-resources-plugin&lt;/artifactId&gt; &lt;version&gt;3.3.1&lt;/version&gt; &lt;executions&gt; &lt;execution&gt; &lt;id&gt;copy-test-classes&lt;/id&gt; &lt;phase&gt;process-test-classes&lt;/phase&gt; &lt;goals&gt; &lt;goal&gt;copy-resources&lt;/goal&gt; &lt;/goals&gt; &lt;configuration&gt; &lt;outputDirectory&gt;${project.build.outputDirectory} &lt;/outputDirectory&gt; &lt;resources&gt; &lt;resource&gt; &lt;directory&gt;${project.build.testOutputDirectory} &lt;/directory&gt; &lt;includes&gt; &lt;include&gt;**/*.class&lt;/include&gt; &lt;/includes&gt; &lt;/resource&gt; &lt;/resources&gt; &lt;/configuration&gt; &lt;/execution&gt; &lt;/executions&gt; &lt;/plugin&gt; &lt;/plugins&gt; &lt;/build&gt; &lt;/profile&gt; &lt;/profiles&gt; &lt;/project&gt; Maven Shade Plugin The Maven Shade Plugin is used to package the project into an executable JAR file. This plugin performs several tasks, such as combining dependencies into a single JAR and modifying the manifest file to specify the main class. Shading phase packs the project into a JAR file that includes dependencies. Configuration: shadedArtifactAttached: When set to true, this ensures the shaded JAR is attached as an additional artifact. transformers: This section modifies the JAR manifest. The ManifestResourceTransformer is used to specify the main class ( eu.cybershu.MainTest). Maven Resources Plugin The Maven Resources Plugin is employed here to ensure that the compiled test classes are included in the final build output. This is necessary because the main class (MainTest) resides in the tests directory. Key Elements: Execution ID (copy-test-classes): Identifies this specific execution of the plugin. Phase (process-test-classes): Specifies when this plugin should run during the build lifecycle. Configuration: outputDirectory: Specifies where the resources should be copied to, typically the main output directory of the build. resources: Defines which resources to copy. Here, it includes all .class files from the test output directory. By configuring this plugin, the build process ensures that the compiled test classes (including MainTest.class) are copied to the main output directory. This step is crucial for including the MainTest class in the final shaded JAR file. Java MainTest.java - Executable Class public class MainTest { public static void main(String[] args) { System.out.println("Let's print a file......"); BufferedReader reader = new BufferedReader(new InputStreamReader( MainTest.class.getClassLoader().getResourceAsStream("any_file.txt"))); reader.lines().forEach(System.out::println); } } Build and running How to build: mvn package -P test-jar How to run: java -jar ./target/mvn-test-jar-1.0-SNAPSHOT-shaded.jar Contact I hope you find this useful! 😊 Please leave comments or contact me directly if you have any questions: Twitter: https://twitter.com/MichalMzr LinkedIn: https://www.linkedin.com/in/michmzr/ Check out more of my posts on my second blog: Geekowojażer.pl]]></summary></entry><entry xml:lang="en"><title type="html">How to work with Python virtual env in Dockerfile</title><link href="https://www.cybershu.eu/articles/python-dockerfile.html" rel="alternate" type="text/html" title="How to work with Python virtual env in Dockerfile" /><published>2024-05-17T00:00:00+02:00</published><updated>2024-05-17T00:00:00+02:00</updated><id>https://www.cybershu.eu/articles/python-dockerfile</id><content type="html" xml:base="https://www.cybershu.eu/articles/python-dockerfile.html"><![CDATA[<p>If you work with Python, you probably need to activate a virtual environment to manage dependencies. Using a virtualenv requires activation each time you want to use Python or pip. This blog post will show you how to effectively and elegantly work with Python when building a Dockerfile.</p>

<h2 id="method-1-bad">Method 1: Bad</h2>

<p>Let’s say you want to copy and change a shell script into a Dockerfile. The solution below won’t work because of the line RUN . /venv/bin/activate. The virtual environment is activated, but it won’t be available in subsequent commands because RUN commands run in separate processes.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> alpine:latest</span>

<span class="k">RUN </span>apk add <span class="nt">--no-cache</span> python3 py3-pip

<span class="c"># Create and use a virtual environment</span>
<span class="k">RUN </span>python3 <span class="nt">-m</span> venv /venv

<span class="c"># This is wrong!</span>
<span class="k">RUN </span><span class="nb">.</span> /venv/bin/activate

<span class="k">COPY</span><span class="s"> . .</span>

<span class="k">RUN </span>pip <span class="nb">install</span> <span class="nt">-r</span> ./requirements.txt
<span class="k">RUN </span>python main.py

</code></pre></div></div>

<p>I get build error:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#0 building with "default" instance using docker-container driver

#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 276B done
#1 DONE 0.0s

#2 [internal] load metadata for docker.io/library/alpine:latest
#2 DONE 0.2s

#3 [internal] load .dockerignore
#3 transferring context: 2B done
#3 DONE 0.0s

#4 [1/7] FROM docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b
#4 resolve docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b done
#4 DONE 0.0s

#5 [internal] load build context
#5 transferring context: 91B done
#5 DONE 0.0s

#6 [2/7] RUN apk add --no-cache python3 py3-pip
#6 CACHED

#7 [3/7] RUN python3 -m venv /venv
#7 CACHED

#8 [4/7] RUN . /venv/bin/activate
#8 CACHED

#9 [5/7] COPY . .
#9 CACHED

#10 [6/7] RUN pip install -r ./requirements.txt
#10 0.587 error: externally-managed-environment
#10 0.587
#10 0.587 × This environment is externally managed
#10 0.587 ╰─&gt;
#10 0.587     The system-wide python installation should be maintained using the system
#10 0.587     package manager (apk) only.
#10 0.587
#10 0.587     If the package in question is not packaged already (and hence installable via
#10 0.587     "apk add py3-somepackage"), please consider installing it inside a virtual
#10 0.587     environment, e.g.:
#10 0.587
#10 0.587     python3 -m venv /path/to/venv
#10 0.587     . /path/to/venv/bin/activate
#10 0.587     pip install mypackage
#10 0.587
#10 0.587     To exit the virtual environment, run:
#10 0.587
#10 0.587     deactivate
#10 0.587
#10 0.587     The virtual environment is not deleted, and can be re-entered by re-sourcing
#10 0.587     the activate file.
#10 0.587
#10 0.587     To automatically manage virtual environments, consider using pipx (from the
#10 0.587     pipx package).
#10 0.587
#10 0.587 note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
#10 0.587 hint: See PEP 668 for the detailed specification.
#10 ERROR: process "/bin/sh -c pip install -r ./requirements.txt" did not complete successfully: exit code: 1
------
 &gt; [6/7] RUN pip install -r ./requirements.txt:
0.587     deactivate
0.587
0.587     The virtual environment is not deleted, and can be re-entered by re-sourcing
0.587     the activate file.
0.587
0.587     To automatically manage virtual environments, consider using pipx (from the
0.587     pipx package).
0.587
0.587 note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
0.587 hint: See PEP 668 for the detailed specification.
------
Dockerfile:13
--------------------
  11 |     COPY . .
  12 |
  13 | &gt;&gt;&gt; RUN pip install -r ./requirements.txt
  14 |     RUN python main.py
  15 |
--------------------
</code></pre></div></div>

<p>The system Python environment is triggered when importing dependencies from requirements.txt, not the created <code class="language-plaintext highlighter-rouge">/venv/</code>!</p>

<h2 id="method-2-ugly">Method 2: Ugly</h2>

<p>You can directly call <code class="language-plaintext highlighter-rouge">/venv/</code>’s Python and pip. It’s a better solution, but it still has caveats. First, you need to do tedious work adding the prefix path. Secondly, when Python triggers a subprocess, it won’t have access to <code class="language-plaintext highlighter-rouge">/venv/</code> dependencies. So, can we do better?</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> alpine:latest</span>

<span class="k">RUN </span>apk add <span class="nt">--no-cache</span> python3 py3-pip

<span class="c"># Create and use a virtual environment</span>
<span class="k">RUN </span>python3 <span class="nt">-m</span> venv /venv

<span class="k">COPY</span><span class="s"> . .</span>

<span class="k">RUN </span>/venv/bin/pip <span class="nb">install</span> <span class="nt">-r</span> ./requirements.txt
<span class="k">RUN </span>/venv/bin/python main.py
</code></pre></div></div>

<h2 id="method-3-good">Method 3: Good</h2>
<p>Yes, we can. The good solution is to add <code class="language-plaintext highlighter-rouge">/venv/bin</code> to the system <code class="language-plaintext highlighter-rouge">PATH</code> environment variable.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> alpine:latest</span>

<span class="k">RUN </span>apk add <span class="nt">--no-cache</span> python3 py3-pip

<span class="c"># Create and use a virtual environment</span>
<span class="k">RUN </span>python3 <span class="nt">-m</span> venv /venv
<span class="k">ENV</span><span class="s"> PATH="/venv/bin:$PATH"</span>

<span class="k">COPY</span><span class="s"> . .</span>
<span class="k">RUN </span>pip <span class="nb">install</span> <span class="nt">-r</span> ./requirements.txt
<span class="k">RUN </span>python main.py
</code></pre></div></div>

<p>As a result, we achieve an elegant solution!</p>

<p>The virtualenv documentation even states that activating the environment is “purely a convenience.”</p>

<p>If you read the code for activate, it does several things:</p>

<ol>
  <li>Figures out what shell you’re running.</li>
  <li>Adds a deactivate function to your shell, which can interfere with pydoc.</li>
  <li>Changes the shell prompt to include the virtualenv name.</li>
  <li>Unsets the <code class="language-plaintext highlighter-rouge">PYTHONHOME</code> environment variable, if it was set.</li>
  <li>
    <p>Sets two environment variables: <code class="language-plaintext highlighter-rouge">VIRTUAL_ENV</code> and <code class="language-plaintext highlighter-rouge">PATH</code>.</p>
  </li>
  <li>From Docker’s perspective, points 1-4 are irrelevant. We only need to take care of <code class="language-plaintext highlighter-rouge">VIRTUAL_ENV</code> and <code class="language-plaintext highlighter-rouge">PATH</code>, which we can define manually.</li>
</ol>

<h2 id="wrap-up">Wrap-up</h2>
<p>I hope what I wrote is useful :). Please leave any comments to let me know. If you have any questions, please feel free to contact me directly on:</p>
<ul>
  <li>Twitter: <a href="https://twitter.com/MichalMzr">https://twitter.com/MichalMzr</a></li>
  <li>LinkedIn: <a href="https://www.linkedin.com/in/michmzr/">https://www.linkedin.com/in/michmzr/</a></li>
</ul>

<p>You can also find my posts on my second blog <a href="https://www.geekowojazer.pl/">Geekowojażer.pl</a></p>]]></content><author><name>Michał Mazur</name></author><category term="python" /><category term="docker" /><category term="dockerfile" /><category term="venv" /><category term="automation" /><category term="devops" /><summary type="html"><![CDATA[If you work with Python, you probably need to activate a virtual environment to manage dependencies. Using a virtualenv requires activation each time you want to use Python or pip. This blog post will show you how to effectively and elegantly work with Python when building a Dockerfile. Method 1: Bad Let’s say you want to copy and change a shell script into a Dockerfile. The solution below won’t work because of the line RUN . /venv/bin/activate. The virtual environment is activated, but it won’t be available in subsequent commands because RUN commands run in separate processes. FROM alpine:latest RUN apk add --no-cache python3 py3-pip # Create and use a virtual environment RUN python3 -m venv /venv # This is wrong! RUN . /venv/bin/activate COPY . . RUN pip install -r ./requirements.txt RUN python main.py I get build error: #0 building with "default" instance using docker-container driver #1 [internal] load build definition from Dockerfile #1 transferring dockerfile: 276B done #1 DONE 0.0s #2 [internal] load metadata for docker.io/library/alpine:latest #2 DONE 0.2s #3 [internal] load .dockerignore #3 transferring context: 2B done #3 DONE 0.0s #4 [1/7] FROM docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b #4 resolve docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b done #4 DONE 0.0s #5 [internal] load build context #5 transferring context: 91B done #5 DONE 0.0s #6 [2/7] RUN apk add --no-cache python3 py3-pip #6 CACHED #7 [3/7] RUN python3 -m venv /venv #7 CACHED #8 [4/7] RUN . /venv/bin/activate #8 CACHED #9 [5/7] COPY . . #9 CACHED #10 [6/7] RUN pip install -r ./requirements.txt #10 0.587 error: externally-managed-environment #10 0.587 #10 0.587 × This environment is externally managed #10 0.587 ╰─&gt; #10 0.587 The system-wide python installation should be maintained using the system #10 0.587 package manager (apk) only. #10 0.587 #10 0.587 If the package in question is not packaged already (and hence installable via #10 0.587 "apk add py3-somepackage"), please consider installing it inside a virtual #10 0.587 environment, e.g.: #10 0.587 #10 0.587 python3 -m venv /path/to/venv #10 0.587 . /path/to/venv/bin/activate #10 0.587 pip install mypackage #10 0.587 #10 0.587 To exit the virtual environment, run: #10 0.587 #10 0.587 deactivate #10 0.587 #10 0.587 The virtual environment is not deleted, and can be re-entered by re-sourcing #10 0.587 the activate file. #10 0.587 #10 0.587 To automatically manage virtual environments, consider using pipx (from the #10 0.587 pipx package). #10 0.587 #10 0.587 note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages. #10 0.587 hint: See PEP 668 for the detailed specification. #10 ERROR: process "/bin/sh -c pip install -r ./requirements.txt" did not complete successfully: exit code: 1 ------ &gt; [6/7] RUN pip install -r ./requirements.txt: 0.587 deactivate 0.587 0.587 The virtual environment is not deleted, and can be re-entered by re-sourcing 0.587 the activate file. 0.587 0.587 To automatically manage virtual environments, consider using pipx (from the 0.587 pipx package). 0.587 0.587 note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages. 0.587 hint: See PEP 668 for the detailed specification. ------ Dockerfile:13 -------------------- 11 | COPY . . 12 | 13 | &gt;&gt;&gt; RUN pip install -r ./requirements.txt 14 | RUN python main.py 15 | -------------------- The system Python environment is triggered when importing dependencies from requirements.txt, not the created /venv/! Method 2: Ugly You can directly call /venv/’s Python and pip. It’s a better solution, but it still has caveats. First, you need to do tedious work adding the prefix path. Secondly, when Python triggers a subprocess, it won’t have access to /venv/ dependencies. So, can we do better? FROM alpine:latest RUN apk add --no-cache python3 py3-pip # Create and use a virtual environment RUN python3 -m venv /venv COPY . . RUN /venv/bin/pip install -r ./requirements.txt RUN /venv/bin/python main.py Method 3: Good Yes, we can. The good solution is to add /venv/bin to the system PATH environment variable. FROM alpine:latest RUN apk add --no-cache python3 py3-pip # Create and use a virtual environment RUN python3 -m venv /venv ENV PATH="/venv/bin:$PATH" COPY . . RUN pip install -r ./requirements.txt RUN python main.py As a result, we achieve an elegant solution! The virtualenv documentation even states that activating the environment is “purely a convenience.” If you read the code for activate, it does several things: Figures out what shell you’re running. Adds a deactivate function to your shell, which can interfere with pydoc. Changes the shell prompt to include the virtualenv name. Unsets the PYTHONHOME environment variable, if it was set. Sets two environment variables: VIRTUAL_ENV and PATH. From Docker’s perspective, points 1-4 are irrelevant. We only need to take care of VIRTUAL_ENV and PATH, which we can define manually. Wrap-up I hope what I wrote is useful :). Please leave any comments to let me know. If you have any questions, please feel free to contact me directly on: Twitter: https://twitter.com/MichalMzr LinkedIn: https://www.linkedin.com/in/michmzr/ You can also find my posts on my second blog Geekowojażer.pl]]></summary></entry><entry xml:lang="en"><title type="html">How to use Kaniko and JFrog with GitHub Actions</title><link href="https://www.cybershu.eu/articles/kaniko-jfrog.html" rel="alternate" type="text/html" title="How to use Kaniko and JFrog with GitHub Actions" /><published>2024-01-16T00:00:00+01:00</published><updated>2024-01-16T00:00:00+01:00</updated><id>https://www.cybershu.eu/articles/kaniko-jfrog-github-actions</id><content type="html" xml:base="https://www.cybershu.eu/articles/kaniko-jfrog.html"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>In this article, we will explore the use of Kaniko and JFrog with GitHub Actions to build and push a Docker image to JFrog Artifactory. This solution addressed issues I encountered while working on a project in my company. I hope it proves useful for you as well.</p>
<h2 id="prerequisites">Prerequisites</h2>

<p>You must have:</p>
<ul>
  <li>GitHub project with Dockerfile</li>
  <li>JFrog Artifactory account with created docker repository</li>
</ul>

<h2 id="assumptions">Assumptions</h2>
<p>Your artifactory repository is called <code class="language-plaintext highlighter-rouge">docker-local</code> and you have created a user with username <code class="language-plaintext highlighter-rouge">docker</code> and password(api token) <code class="language-plaintext highlighter-rouge">docker-password</code>.
Full path is <code class="language-plaintext highlighter-rouge">https://artifactory.example.com/artifactory/docker-images</code> and you want to build and push image with name <code class="language-plaintext highlighter-rouge">my-image:latest</code>.</p>

<h2 id="configuration">Configuration</h2>

<p>I use <a href="https://github.com/aevea/action-kaniko">kaniko</a> action to build and push image to JFrog Artifactory.</p>

<p><strong>Example Github action job file:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="na">name</span><span class="pi">:</span> <span class="s">Build and push Docker image to JFrog Artifactory</span>
  <span class="na">jobs</span><span class="pi">:</span>
  <span class="na">container-test-job</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Kaniko build</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">aevea/action-kaniko@master</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">image</span><span class="pi">:</span> <span class="s">docker-images/my-image</span>
          <span class="na">registry</span><span class="pi">:</span> <span class="s">artifactory.example.com</span>
          <span class="na">username</span><span class="pi">:</span> <span class="s2">"</span><span class="s">docker"</span>
          <span class="na">password</span><span class="pi">:</span> <span class="s2">"</span><span class="s">docker-password"</span>
          <span class="na">tag</span><span class="pi">:</span> <span class="s">latest</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">Dockerfile</span>
      <span class="pi">-</span> <span class="na">run</span><span class="pi">:</span> <span class="s">docker images ls</span>
</code></pre></div></div>

<p>you can alternatively use docker build command:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="na">name</span><span class="pi">:</span> <span class="s">Build and push Docker image to JFrog Artifactory</span>
  <span class="na">jobs</span><span class="pi">:</span>
  <span class="na">container-test-job</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">docker build</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">docker/build-push-action@v5</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">push</span><span class="pi">:</span> <span class="no">false</span>
          <span class="na">tags</span><span class="pi">:</span> <span class="s">my-image:latest</span>
          <span class="na">file</span><span class="pi">:</span> <span class="s">Dockerfile</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Login to DevHub Docker Hub</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">docker/login-action@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">registry</span><span class="pi">:</span> <span class="s2">"</span><span class="s">artifactory.example.com"</span>
          <span class="na">username</span><span class="pi">:</span> <span class="s2">"</span><span class="s">docker"</span>
          <span class="na">password</span><span class="pi">:</span> <span class="s2">"</span><span class="s">docker-password"</span>
      <span class="pi">-</span> <span class="na">run</span><span class="pi">:</span> <span class="s">docker image ls</span>
      <span class="pi">-</span> <span class="na">run</span><span class="pi">:</span> <span class="s">docker tag my-image:latest artifactory.example.com/docker-images/my-image:latest</span>
      <span class="pi">-</span> <span class="na">run</span><span class="pi">:</span> <span class="s">docker push artifactory.example.com/docker-images/my-image:latest</span>
</code></pre></div></div>

<h2 id="contact">Contact</h2>
<p>I hope what I wrote is useful :). Please leave any comments to let me know. If you have any questions, please feel free to contact me directly on:</p>
<ul>
  <li>Twitter: <a href="https://twitter.com/MichalMzr">https://twitter.com/MichalMzr</a></li>
  <li>LinkedIn: <a href="https://www.linkedin.com/in/michmzr/">https://www.linkedin.com/in/michmzr/</a></li>
</ul>

<p>You can also find my posts on my second blog <a href="https://www.geekowojazer.pl/">Geekowojażer.pl</a></p>]]></content><author><name>Michał Mazur</name></author><category term="kaniko" /><category term="jfrog" /><category term="github" /><category term="actions" /><category term="artifactory" /><category term="devops" /><category term="docker" /><category term="ci" /><category term="cd" /><category term="github-actions" /><summary type="html"><![CDATA[Introduction In this article, we will explore the use of Kaniko and JFrog with GitHub Actions to build and push a Docker image to JFrog Artifactory. This solution addressed issues I encountered while working on a project in my company. I hope it proves useful for you as well. Prerequisites You must have: GitHub project with Dockerfile JFrog Artifactory account with created docker repository Assumptions Your artifactory repository is called docker-local and you have created a user with username docker and password(api token) docker-password. Full path is https://artifactory.example.com/artifactory/docker-images and you want to build and push image with name my-image:latest. Configuration I use kaniko action to build and push image to JFrog Artifactory. Example Github action job file: name: Build and push Docker image to JFrog Artifactory jobs: container-test-job: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Kaniko build uses: aevea/action-kaniko@master with: image: docker-images/my-image registry: artifactory.example.com username: "docker" password: "docker-password" tag: latest path: Dockerfile - run: docker images ls you can alternatively use docker build command: name: Build and push Docker image to JFrog Artifactory jobs: container-test-job: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: docker build uses: docker/build-push-action@v5 with: push: false tags: my-image:latest file: Dockerfile - name: Login to DevHub Docker Hub uses: docker/login-action@v3 with: registry: "artifactory.example.com" username: "docker" password: "docker-password" - run: docker image ls - run: docker tag my-image:latest artifactory.example.com/docker-images/my-image:latest - run: docker push artifactory.example.com/docker-images/my-image:latest Contact I hope what I wrote is useful :). Please leave any comments to let me know. If you have any questions, please feel free to contact me directly on: Twitter: https://twitter.com/MichalMzr LinkedIn: https://www.linkedin.com/in/michmzr/ You can also find my posts on my second blog Geekowojażer.pl]]></summary></entry></feed>