<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on besserentwickeln.de</title><link>https://besserentwickeln.de/post/</link><description>Recent content in Posts on besserentwickeln.de</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>Philipp Tuchardt</copyright><lastBuildDate>Mon, 28 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://besserentwickeln.de/post/index.xml" rel="self" type="application/rss+xml"/><item><title>Proxy per IP-Selbsttest finden</title><link>https://besserentwickeln.de/p/proxy-per-ip-selbsttest-finden/</link><pubDate>Mon, 28 Sep 2026 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/proxy-per-ip-selbsttest-finden/</guid><description>&lt;p>Wenn eine Anwendung wie für jeden Request dieselbe IP protokolliert oder Rate-Limits plötzlich alle
Nutzer gleichzeitig treffen, steht oft ein Proxy oder Load Balancer zwischen Client und Server.
Ob das so ist, und welche IP dieser Proxy hat, lässt sich mit einem einfachen Selbsttest herausfinden.&lt;/p>
&lt;h2 id="die-idee">Die Idee
&lt;/h2>&lt;p>Du kennst deine eigene öffentliche IP. Du schickst einen Request an den Server und schaust nach, welche IP
dort protokolliert wurde.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Beide IPs stimmen überein:&lt;/strong> Die Anwendung sieht die echte Client-IP. Entweder steht kein Proxy dazwischen
oder er wird bereits korrekt ausgewertet.&lt;/li>
&lt;li>&lt;strong>Im Log steht eine andere IP:&lt;/strong> Du hast den Proxy gefunden und siehst direkt seine IP.&lt;/li>
&lt;/ul>
&lt;h2 id="eigene-ip-ermitteln-und-request-senden">Eigene IP ermitteln und Request senden
&lt;/h2>&lt;p>Auf deinem Rechner:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -s https://api.ipify.org
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -s -o /dev/null -w &lt;span class="s1">&amp;#39;%{http_code}\n&amp;#39;&lt;/span> https://example.com/ip-probe-a1b2c3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Der erste Befehl gibt deine öffentliche IP aus. Die zweite URL existiert nicht – sie liefert einen &lt;code>404&lt;/code> und
löst keinerlei Fachlogik aus. Der eindeutige Pfad sorgt dafür, dass du die Zeile im Log später sicher wiederfindest.&lt;/p>
&lt;h2 id="access-log-auf-dem-server-prüfen">Access-Log auf dem Server prüfen
&lt;/h2>&lt;p>Per SSH auf den Server verbinden:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ssh user@example.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Das Access-Log finden – der Pfad hängt vom Hoster ab:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ls -la ~/logs/ /var/log/nginx/ /var/log/apache2/ 2&amp;gt;/dev/null
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Und die passende Zeile heraussuchen:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">grep ip-probe-a1b2c3 ~/logs/*access*.log &lt;span class="p">|&lt;/span> tail -5
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Die IP am Anfang der Zeile vergleichst du nun mit der Ausgabe von &lt;code>api.ipify.org&lt;/code>.&lt;/p>
&lt;h2 id="optimierungen">Optimierungen
&lt;/h2>&lt;p>Hast du Ergänzungen oder einen besseren Weg? Schreib mir gerne eine Mail an
&lt;a class="link" href="mailto:optimierungen@besserentwickeln.de" >optimierungen@besserentwickeln.de&lt;/a>&lt;/p></description></item><item><title>Datei aus dem letzten Commit entfernen, Inhalt aber behalten</title><link>https://besserentwickeln.de/p/datei-aus-dem-letzten-commit-entfernen-inhalt-aber-behalten/</link><pubDate>Tue, 15 Sep 2026 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/datei-aus-dem-letzten-commit-entfernen-inhalt-aber-behalten/</guid><description>&lt;p>Man committet und merkt danach, dass eine Datei im Commit gelandet ist, die dort nichts zu suchen hat.
Die Änderung selbst will man aber behalten.&lt;/p>
&lt;h2 id="how-to">How To
&lt;/h2>&lt;p>Die Datei aus dem Commit-Inhalt lösen, ihren aktuellen Inhalt aber behalten:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git restore --source&lt;span class="o">=&lt;/span>HEAD~1 --staged myFile.php
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git commit --amend --no-edit
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>git restore --source=HEAD~1 --staged&lt;/code> setzt die Datei im Index auf den Stand vor dem Commit
zurück. Das Arbeitsverzeichnis bleibt dabei unangetastet. Der Inhalt der Datei geht also nicht
verloren. Das anschließende &lt;code>git commit --amend --no-edit&lt;/code> schreibt den Commit ohne die Datei neu,
die Commit-Message bleibt unverändert.&lt;/p>
&lt;h2 id="ergebnis">Ergebnis
&lt;/h2>&lt;p>Der bisherige Commit wird durch einen neuen Commit ohne die README ersetzt. Die Datei
taucht danach wieder als unstaged Änderung in &lt;code>git status&lt;/code> auf.&lt;/p>
&lt;p>Wichtig: &lt;code>--amend&lt;/code> erzeugt einen neuen Commit-Hash. Wenn der alte Commit bereits gepusht wurde,
braucht es einen Force-Push.&lt;/p>
&lt;h2 id="verifikation">Verifikation
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git show --stat HEAD
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git status -s
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>git show --stat HEAD&lt;/code> zeigt, welche Dateien tatsächlich im Commit stecken — die entfernte Datei
darf hier nicht mehr auftauchen. &lt;code>git status -s&lt;/code> zeigt sie dagegen wieder als Änderung im
Arbeitsverzeichnis.&lt;/p></description></item><item><title>Formulare dauerhaft per DevTools Snippet blockieren</title><link>https://besserentwickeln.de/p/formulare-dauerhaft-per-devtools-snippet-blockieren/</link><pubDate>Tue, 07 Apr 2026 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/formulare-dauerhaft-per-devtools-snippet-blockieren/</guid><description>&lt;p>Manchmal möchte man beim Entwickeln oder Debuggen verhindern, dass ein Formular abgeschickt wird –
ohne jedes Mal den Quellcode anzupassen oder einen Breakpoint zu setzen.
Als DevTools Snippet lässt sich das einmal ablegen und dann nach jedem Seitenladen mit einem
Klick erneut ausführen.&lt;/p>
&lt;h2 id="snippet-anlegen">Snippet anlegen
&lt;/h2>&lt;p>Im Chrome DevTools unter &lt;strong>Sources → Snippets&lt;/strong> ein neues Snippet erstellen und folgenden Code einfügen:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;submit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">e&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">preventDefault&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Anschließend mit &lt;strong>Strg + Enter&lt;/strong> (bzw. &lt;strong>Cmd + Enter&lt;/strong>) ausführen.&lt;/p>
&lt;h2 id="warum-das-funktioniert">Warum das funktioniert
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Global&lt;/strong> – der Listener hängt am &lt;code>document&lt;/code> und greift damit auf alle Formulare der Seite.&lt;/li>
&lt;li>&lt;strong>Capture-Phase (&lt;code>true&lt;/code>)&lt;/strong> – der Listener läuft, bevor der Event die Handler am Formular
selbst erreicht. &lt;code>e.preventDefault()&lt;/code> unterbindet dann das Standardverhalten des Browsers,
also das eigentliche Abschicken bzw. das Neuladen der Seite.&lt;/li>
&lt;/ul>
&lt;p>&lt;code>preventDefault()&lt;/code> stoppt allerdings &lt;strong>nicht&lt;/strong> die weitere Ausbreitung des Events – Handler von
React, Vue &amp;amp; Co. laufen trotzdem noch. Solange die nur den nativen Submit abfangen, ist das
egal. Verschickt ein Framework-Handler die Daten aber selbst per &lt;code>fetch&lt;/code>/&lt;code>XMLHttpRequest&lt;/code>, muss
man die Ausbreitung zusätzlich unterbinden:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;submit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">e&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">preventDefault&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stopImmediatePropagation&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="vorteil-gegenüber-breakpoints">Vorteil gegenüber Breakpoints
&lt;/h2>&lt;p>Ein Snippet ist schnell ausgeführt, muss nicht jedes Mal neu konfiguriert werden und lässt sich
problemlos erweitern – zum Beispiel um ein &lt;code>console.log&lt;/code>, das die Formulardaten ausgibt:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;submit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">e&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">preventDefault&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Form submit blocked:&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>So sieht man sofort, welches Formular betroffen wäre, ohne dass eine Anfrage rausgeht.&lt;/p>
&lt;h2 id="schnelle-alternative-console">Schnelle Alternative: Console
&lt;/h2>&lt;p>Wer das nur einmalig ausprobieren möchte, kann den Code auch direkt in die &lt;strong>Console&lt;/strong> (Tab &lt;strong>Console&lt;/strong>) einfügen und mit Enter bestätigen.
Der Effekt ist identisch. In beiden Fällen ist der Listener nach dem nächsten Seitenneuladen weg –
der Unterschied: Das Snippet bleibt im DevTools gespeichert und lässt sich mit einem Klick erneut
ausführen, während man den Console-Befehl jedes Mal neu einfügen muss.&lt;/p></description></item><item><title>Ai Commits in IDEs von Jetbrains</title><link>https://besserentwickeln.de/p/ai-commits-in-ides-von-jetbrains/</link><pubDate>Wed, 02 Jul 2025 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/ai-commits-in-ides-von-jetbrains/</guid><description>&lt;p>Seit einiger Zeit nutzte ich die meisten der built-in AI-Features von PhpStorm bzw. allgemein
den IDEs von Jetbrains. Dabei auch die Funktion Commits via AI-Prompt zu erstellen.&lt;/p>
&lt;h2 id="anforderung">Anforderung
&lt;/h2>&lt;p>Da ich mich in Vergangenheit versucht habe die meiste Zeit an die &lt;a class="link" href="https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md" target="_blank" rel="noopener"
>angular Git Commit Guidelines
&lt;/a> zu halten, war das auch das
Ziel meines Prompts.&lt;/p>
&lt;h2 id="der-prompt">Der Prompt
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Build the commit message after the following structure:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;type&amp;gt;(&amp;lt;scope&amp;gt;): &amp;lt;short summary&amp;gt;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Types are: build|ci|docs|feat|fix|perf|refactor|test.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The scope is the commit scope,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">one word for example &amp;#34;header&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Use camelCase for the scope if needed.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Summary in present tense.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Not capitalized. No period at the end.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="verwendung-in-phpstorm">Verwendung in PhpStorm
&lt;/h2>&lt;p>Damit der Prompt zum Einsatz kommen kann, fügt ihn an folgender Stelle ein.&lt;/p>
&lt;p>&lt;img src="https://besserentwickeln.de/p/ai-commits-in-ides-von-jetbrains/phpstorm-settings.png"
width="1092"
height="1060"
srcset="https://besserentwickeln.de/p/ai-commits-in-ides-von-jetbrains/phpstorm-settings_hu_99b8ba79c2c873a5.png 480w, https://besserentwickeln.de/p/ai-commits-in-ides-von-jetbrains/phpstorm-settings_hu_102e6943488501f4.png 1024w"
loading="lazy"
alt="Einstellungen"
class="gallery-image"
data-flex-grow="103"
data-flex-basis="247px"
>&lt;/p>
&lt;p>Anschließend müsst ihr nur noch die Files auswählen die ihr commiten wollt und klickt auf den kleinen lila Strudel über
der Box wo eure Commit-Message erscheinen soll&lt;/p>
&lt;p>&lt;img src="https://besserentwickeln.de/p/ai-commits-in-ides-von-jetbrains/generate-commit.png"
width="729"
height="466"
srcset="https://besserentwickeln.de/p/ai-commits-in-ides-von-jetbrains/generate-commit_hu_5ce525bf2cc3f9b3.png 480w, https://besserentwickeln.de/p/ai-commits-in-ides-von-jetbrains/generate-commit_hu_36f95ec728511549.png 1024w"
loading="lazy"
alt="Commit erstellen"
class="gallery-image"
data-flex-grow="156"
data-flex-basis="375px"
>&lt;/p>
&lt;h2 id="resultat">Resultat
&lt;/h2>&lt;p>Eure Commit-Messages sollten nun automatisch nach diesem Schema erstellt werden.&lt;/p>
&lt;p>&lt;img src="https://besserentwickeln.de/p/ai-commits-in-ides-von-jetbrains/result.png"
width="729"
height="466"
srcset="https://besserentwickeln.de/p/ai-commits-in-ides-von-jetbrains/result_hu_a874ff5951501cc0.png 480w, https://besserentwickeln.de/p/ai-commits-in-ides-von-jetbrains/result_hu_bf57da400e4c34eb.png 1024w"
loading="lazy"
alt="Ergebnis"
class="gallery-image"
data-flex-grow="156"
data-flex-basis="375px"
>&lt;/p>
&lt;h2 id="optimierungen">Optimierungen
&lt;/h2>&lt;p>Dieser kleine Guide ist relativ schnell entstanden, ich denke es gibt sicherlich noch mögliche Optimierungen.
Wenn du etwas hast, lass es mich gerne wissen und schreib mir eine Mail an &lt;a class="link" href="mailto:optimierungen@besserentwickeln.de" >optimierungen@besserentwickeln.de&lt;/a>&lt;/p></description></item><item><title>Twig Macros</title><link>https://besserentwickeln.de/p/twig-macros/</link><pubDate>Thu, 13 Mar 2025 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/twig-macros/</guid><description>&lt;h1 id="eine-einsteiger-anleitung-zu-twig-macros">Eine Einsteiger-Anleitung zu Twig Macros
&lt;/h1>&lt;h2 id="was-sind-twig-macros">Was sind Twig Macros?
&lt;/h2>&lt;p>Twig Macros sind wiederverwendbare Funktionen innerhalb von Twig-Templates. Sie helfen dabei, Code zu vereinfachen, indem sie Wiederholungen reduzieren und die Wartbarkeit verbessern – ähnlich wie Funktionen in Programmiersprachen.&lt;/p>
&lt;h2 id="ein-macro-definieren">Ein Macro definieren
&lt;/h2>&lt;p>Ein Twig Macro wird mit dem Tag &lt;code>{% macro %}&lt;/code> definiert. Es kann, genau wie eine Funktion, Parameter entgegennehmen.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-twig" data-lang="twig">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{%&lt;/span> &lt;span class="k">macro&lt;/span> &lt;span class="nv">button&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">text&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nv">url&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nv">class&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;a href=&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span> &lt;span class="nv">url&lt;/span> &lt;span class="cp">}}&lt;/span>&lt;span class="x">&amp;#34; class=&amp;#34;btn &lt;/span>&lt;span class="cp">{{&lt;/span> &lt;span class="nv">class&lt;/span> &lt;span class="cp">}}&lt;/span>&lt;span class="x">&amp;#34;&amp;gt;&lt;/span>&lt;span class="cp">{{&lt;/span> &lt;span class="nv">text&lt;/span> &lt;span class="cp">}}&lt;/span>&lt;span class="x">&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endmacro&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dieses Macro erzeugt eine Button-Komponente, die mehrfach mit unterschiedlichen Werten wiederverwendet werden kann.&lt;/p>
&lt;h2 id="macros-im-selben-template-verwenden">Macros im selben Template verwenden
&lt;/h2>&lt;p>Auch innerhalb der Datei, in der das Macro definiert ist, muss es zuerst importiert werden.
&lt;code>{% import _self as macros %}&lt;/code> macht die eigenen Macros unter dem Namespace &lt;code>macros&lt;/code> verfügbar:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-twig" data-lang="twig">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{%&lt;/span> &lt;span class="k">import&lt;/span> &lt;span class="nv">_self&lt;/span> &lt;span class="nv">as&lt;/span> &lt;span class="nv">macros&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{{&lt;/span> &lt;span class="nv">macros.button&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s1">&amp;#39;Click me&amp;#39;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="s1">&amp;#39;https://example.com&amp;#39;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="s1">&amp;#39;btn-primary&amp;#39;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="cp">}}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>Der frühere Aufruf über &lt;code>{{ _self.button(...) }}&lt;/code> ist seit Twig 2.11 veraltet und
funktioniert in Twig 3 (und damit in aktuellen Symfony-/Shopware-Versionen) nicht mehr.&lt;/p>&lt;/blockquote>
&lt;h2 id="macros-in-einer-separaten-datei-speichern">Macros in einer separaten Datei speichern
&lt;/h2>&lt;p>Für eine bessere Organisation können Macros in einer separaten Datei (z. B. &lt;code>macros.twig&lt;/code>) gespeichert und bei Bedarf importiert werden.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-twig" data-lang="twig">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{%&lt;/span> &lt;span class="k">import&lt;/span> &lt;span class="s1">&amp;#39;macros.twig&amp;#39;&lt;/span> &lt;span class="nv">as&lt;/span> &lt;span class="nv">macros&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{{&lt;/span> &lt;span class="nv">macros.button&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s1">&amp;#39;Learn More&amp;#39;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="s1">&amp;#39;https://example.com&amp;#39;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="s1">&amp;#39;btn-secondary&amp;#39;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="cp">}}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="vorteile-von-twig-macros">Vorteile von Twig Macros
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Code-Wiederverwendung&lt;/strong>: Vermeidet das Duplizieren gleicher Strukturen.&lt;/li>
&lt;li>&lt;strong>Bessere Lesbarkeit&lt;/strong>: Hält Templates sauberer und strukturierter.&lt;/li>
&lt;li>&lt;strong>Einfache Wartung&lt;/strong>: Macros einmal aktualisieren und die Änderungen überall übernehmen.&lt;/li>
&lt;/ul></description></item><item><title>Deployment einer statischen Seite</title><link>https://besserentwickeln.de/p/deployment-einer-statischen-seite/</link><pubDate>Tue, 11 Mar 2025 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/deployment-einer-statischen-seite/</guid><description>&lt;p>In diesem Blogpost schauen wir uns an, wie wir eine einfache statische Seite
via &lt;a class="link" href="https://gitlab.com" target="_blank" rel="noopener"
>gitlab.com&lt;/a> deployt.&lt;/p>
&lt;hr>
&lt;h2 id="vorraussetzungen">Vorraussetzungen
&lt;/h2>&lt;ul>
&lt;li>Account bei &lt;a class="link" href="https://gitlab.com" target="_blank" rel="noopener"
>gitlab.com&lt;/a>&lt;/li>
&lt;li>Ein Repository in welchem man Administrator ist&lt;/li>
&lt;li>Git-Bash oder ein anderes Linux-Terminal, wenn auf Windows&lt;/li>
&lt;/ul>
&lt;h2 id="ssh-keys">SSH-Keys
&lt;/h2>&lt;h3 id="erstellen-eines-ssh-keys">Erstellen eines SSH-Keys
&lt;/h3>&lt;p>Um später den Gitlab-Runner zu berrechtigen, dass dieser per SSH auf den Ziel-Server zugreifen darf,
erstellen wir ein neues Key-Pair, welches ausschließlich dafür verwendet wird.&lt;/p>
&lt;p>Dieses Keypair erstellen ich nach dem vom &lt;a class="link" href="https://martin.leyrer.priv.at/" target="_blank" rel="noopener"
>Martin Leyrer&lt;/a>
empfohlenen Aufbau aus seinem Talk &lt;a class="link" href="https://www.youtube.com/watch?v=qvdlLTyUJ5I" target="_blank" rel="noopener"
>&amp;ldquo;Besser Leben mit SSH&amp;rdquo;&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ssh-keygen -t ed25519 -a &lt;span class="m">420&lt;/span> -f ~/your/own/path/id_ed25519 -C &lt;span class="s2">&amp;#34;foo@ba.de&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Bitte nicht vergessen den Pfad &lt;code>~/your/own/path/...&lt;/code> zu ersetzen, da ihr ansonsten, unter Umständenen
euren bereits vorhanden SSH-Key durch den Deployment-Key ersetzt. Die Dummy-Mail &lt;code>foo@ba.de&lt;/code> sollte
ebenso durch eine passende von euch getauscht werden. Beispielsweise: &lt;code>deployment@euredomain.com&lt;/code> oder &lt;code>noreply@euredomain.com&lt;/code>.&lt;/p>
&lt;h3 id="hinterlegen-des-ssh-keys">Hinterlegen des SSH-Keys
&lt;/h3>&lt;p>Nachdem das Keypair erzeugt wurde, kann der Public-Key auf eurem Zielserver hinterlegt werden.
Je nach Hoster kann dies über GUI oder per SSH/FTP geschehen.&lt;/p>
&lt;p>Um euch den Public-Key auszugeben, könnt ihr im Terminal den &lt;code>cat&lt;/code>-Befehl nutzen.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cat ~/path/to/your/key/id_ed25519.pub
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Anschließend diesen bei eurem Hoster hinterlegen. Sollte das per SSH oder FTP geschehen, dann müsst ihr euren Public-Key
in die &lt;code>authorized_keys&lt;/code> kopieren, die findet ihr meist unter &lt;code>~/.ssh/&lt;/code>.&lt;/p>
&lt;h2 id="repository-auf-dem-server-auschecken">Repository auf dem Server auschecken
&lt;/h2>&lt;p>Mittels eines Deploy-Tokens werden wir unseren Ziel-Server berechtigen auf das Gitlab-Repository zuzugreifen.&lt;/p>
&lt;h3 id="deploy-token-erstellen">Deploy Token erstellen
&lt;/h3>&lt;p>Um ein Token zu erstellen navigieren wir in Gitlab wie zu &lt;em>Settings &amp;gt; Repository &amp;gt; Deploy Token&lt;/em>.
Dort klicken wir auf &lt;em>Add token&lt;/em> und erhalten die Ansicht aus dem Screenshot.&lt;/p>
&lt;p>In dieser Ansicht vergeben wir einen Namen und Usernamen für das Token, sowie alle Leserechte. Schreibrechte werden in
Regel nicht benötigt.&lt;/p>
&lt;p>&lt;img src="https://besserentwickeln.de/p/deployment-einer-statischen-seite/deploy-token.png"
width="1153"
height="817"
srcset="https://besserentwickeln.de/p/deployment-einer-statischen-seite/deploy-token_hu_c8ca2d5c97547d16.png 480w, https://besserentwickeln.de/p/deployment-einer-statischen-seite/deploy-token_hu_c1e1425848cc44de.png 1024w"
loading="lazy"
alt="image"
class="gallery-image"
data-flex-grow="141"
data-flex-basis="338px"
>&lt;/p>
&lt;p>Nach Klick auf &lt;em>Create Deploy Token&lt;/em> bekommen wir unseren gewählten Usernamen sowie ein zufälliges Passwort ausgegeben.&lt;/p>
&lt;h3 id="git-clone-ausführen">Git clone ausführen
&lt;/h3>&lt;p>Nun nachdem wir das Deploy-Token haben, können wir unser Repository auf dem Server auschecken. Dazu verbinden wir uns
per SSH mit dem Server und klonen am gewünschten Ort unser Repository.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://tokenName:tokenPassword@gitlab.com/repo.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="gitlab-ciyml-anlegen">gitlab-ci.yml anlegen
&lt;/h3>&lt;p>Nun legen wir die &lt;code>gitlab-ci.yml&lt;/code> an und hinterlegen die notigen Variablen in Gitlab.&lt;/p>
&lt;p>Nachfolgend habe ich ein Template vorbereitet, welches die Variablen &lt;code>SSH_PRIVATE_KEY&lt;/code> und &lt;code>SSH_CONNECTION&lt;/code> erwartet.
Diese Variablen müssen in Gitlab in eurem Repository unter Settings &amp;gt; CI/CD &amp;gt; Variables hinterlegt werden.&lt;/p>
&lt;p>&lt;img src="https://besserentwickeln.de/p/deployment-einer-statischen-seite/gitlab-ci-cd.png"
width="1301"
height="507"
srcset="https://besserentwickeln.de/p/deployment-einer-statischen-seite/gitlab-ci-cd_hu_81e01dc743b81d21.png 480w, https://besserentwickeln.de/p/deployment-einer-statischen-seite/gitlab-ci-cd_hu_df68a4100cb95cb2.png 1024w"
loading="lazy"
alt="image"
class="gallery-image"
data-flex-grow="256"
data-flex-basis="615px"
>&lt;/p>
&lt;p>Wenn diese Variablen hinterlegt sind, könnt ihr anschließende &lt;code>gitlab-ci.yml&lt;/code> zu eurem Projekt hinzufügen.
Diese ist so vorkonfiguriet, dass sie beim Merge nach &lt;code>dev&lt;/code> auf euer DEV-System deployt und mittels &lt;code>release-...&lt;/code> Tag auf &lt;code>live&lt;/code>
deployt.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">stages&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">Deploy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">deploy-dev&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">stage&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Deploy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">rules&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;$CI_COMMIT_REF_NAME == &amp;#34;dev&amp;#34;&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;edbizarro/gitlab-ci-pipeline-php:8.3&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">before_script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">eval $(ssh-agent -s)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">echo &amp;#34;$SSH_PRIVATE_KEY&amp;#34; | tr -d &amp;#39;\r&amp;#39; | ssh-add -&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">mkdir -p ~/.ssh&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">chmod 700 ~/.ssh&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">ssh -o StrictHostKeyChecking=no &amp;#34;$SSH_CONNECTION&amp;#34; &amp;#34;cd /root-project-dir-dev/ &amp;amp;&amp;amp; git fetch &amp;amp;&amp;amp; git checkout dev &amp;amp;&amp;amp; git pull&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">deploy-live&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">stage&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Deploy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">rules&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;$CI_COMMIT_TAG =~ /release-.*/&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;edbizarro/gitlab-ci-pipeline-php:8.3&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">before_script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">eval $(ssh-agent -s)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">echo &amp;#34;$SSH_PRIVATE_KEY&amp;#34; | tr -d &amp;#39;\r&amp;#39; | ssh-add -&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">mkdir -p ~/.ssh&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">chmod 700 ~/.ssh&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">ssh -o StrictHostKeyChecking=no &amp;#34;$SSH_CONNECTION&amp;#34; &amp;#34;cd /root-project-dir-live/ &amp;amp;&amp;amp; git fetch &amp;amp;&amp;amp; git checkout $CI_COMMIT_TAG&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>Symbolische Links unter Linux erstellen: Eine sichere und effektive Anleitung</title><link>https://besserentwickeln.de/p/symbolische-links-unter-linux-erstellen-eine-sichere-und-effektive-anleitung/</link><pubDate>Tue, 11 Mar 2025 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/symbolische-links-unter-linux-erstellen-eine-sichere-und-effektive-anleitung/</guid><description>&lt;h1 id="symbolische-links-unter-linux-erstellen-eine-sichere-und-effektive-anleitung">Symbolische Links unter Linux erstellen: Eine sichere und effektive Anleitung
&lt;/h1>&lt;p>Symbolische Links (Symlinks) sind eine mächtige Funktion unter Linux, mit der du Verknüpfungen zu Dateien oder Verzeichnissen erstellen kannst. Das kann die Dateiverwaltung effizienter und flexibler machen. In dieser Anleitung schauen wir uns an, wie man Symlinks sicher erstellt und dabei häufige Fallstricke vermeidet.&lt;/p>
&lt;h2 id="was-ist-ein-symbolischer-link">Was ist ein symbolischer Link?
&lt;/h2>&lt;p>Ein symbolischer Link ist eine spezielle Art von Datei, die auf eine andere Datei oder ein anderes Verzeichnis verweist. Anders als Hardlinks, die auf denselben Inode wie die Originaldatei verweisen, speichern Symlinks lediglich den Pfad zum Ziel. Das macht sie besonders nützlich, um Dateien über verschiedene Speicherorte hinweg zu organisieren.&lt;/p>
&lt;h2 id="wie-man-einen-symbolischen-link-erstellt">Wie man einen symbolischen Link erstellt
&lt;/h2>&lt;p>Um unter Linux einen symbolischen Link zu erstellen, wird der Befehl &lt;code>ln -s&lt;/code> mit folgender Syntax verwendet:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ln -s &lt;span class="o">[&lt;/span>target&lt;span class="o">]&lt;/span> &lt;span class="o">[&lt;/span>link_name&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Um zum Beispiel einen Symlink für ein Verzeichnis unter &lt;code>/home/user/documents/&lt;/code> auf dem Desktop zu erstellen, führst du aus:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ln -s /home/user/documents/ /home/user/Desktop/documents
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Das erstellt eine Verknüpfung zum Verzeichnis &lt;code>documents&lt;/code> auf dem Desktop, während das ursprüngliche Verzeichnis unverändert bleibt.&lt;/p>
&lt;h3 id="best-practices-für-die-sichere-nutzung-von-symlinks">Best Practices für die sichere Nutzung von Symlinks
&lt;/h3>&lt;p>Um Fehler zu vermeiden und Zuverlässigkeit sicherzustellen, sollten folgende Best Practices befolgt werden:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Zielpfad überprüfen&lt;/strong>
Bevor ein Symlink erstellt wird, sicherstellen, dass die Zieldatei oder das Zielverzeichnis existiert. Dazu Befehle wie &lt;code>ls&lt;/code> oder &lt;code>file&lt;/code> nutzen:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ls -l /home/user/documents/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Absolute Pfade verwenden&lt;/strong>
Relative Pfade funktionieren zwar auch, aber absolute Pfade sorgen für Konsistenz und verhindern Probleme, falls der Symlink in unterschiedlichen Kontexten verwendet wird.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Auf bestehende Dateien oder Links prüfen&lt;/strong>
Sicherstellen, dass am Zielort nicht bereits eine Datei oder ein Verzeichnis mit demselben Namen existiert. Falls nötig, die vorhandene Datei vor dem Erstellen des Symlinks umbenennen oder entfernen:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">rm -r /home/user/Desktop/documents
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Den Symlink testen&lt;/strong>
Nach der Erstellung prüfen, ob der Symlink korrekt funktioniert:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ls -l /home/user/Desktop/documents
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Wird das Zielverzeichnis gelöscht, ist der Symlink defekt (er erscheint in den meisten Terminals rot). Einen defekten Symlink kannst du entfernen mit:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">rm /home/user/Desktop/documents
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="häufige-anwendungsfälle-für-symlinks">Häufige Anwendungsfälle für Symlinks
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Konfigurationsdateien organisieren:&lt;/strong> Symlinks zu häufig genutzten Konfigurationsdateien erstellen, die zentral gespeichert sind.&lt;/li>
&lt;li>&lt;strong>Softwareentwicklung:&lt;/strong> Symlinks nutzen, um verschiedene Versionen von Bibliotheken zu verwalten, ohne Pfade zu ändern.&lt;/li>
&lt;li>&lt;strong>Geteilte Ordner:&lt;/strong> Gemeinsam genutzte Verzeichnisse mit Benutzer-Home-Verzeichnissen verknüpfen für schnellen Zugriff.&lt;/li>
&lt;/ul>
&lt;h2 id="fazit">Fazit
&lt;/h2>&lt;p>Symbolische Links sind ein einfaches, aber mächtiges Werkzeug für effizientes Dateimanagement unter Linux. Wer sich an Best Practices hält, sorgt dafür, dass die eigenen Symlinks zuverlässig und effektiv bleiben. Egal ob für Organisation, Komfort oder Systemkonfiguration – Symlinks können den eigenen Workflow deutlich verbessern.&lt;/p></description></item><item><title>Bootstrap Spacing</title><link>https://besserentwickeln.de/p/bootstrap-spacing/</link><pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/bootstrap-spacing/</guid><description>&lt;h1 id="scss-spacer-in-bootstrap-verstehen">SCSS-Spacer in Bootstrap verstehen
&lt;/h1>&lt;p>Beim Arbeiten mit Bootstrap ist Spacing ein entscheidender Aspekt für gut strukturierte und optisch ansprechende Layouts. Bootstrap bietet ein flexibles System zur Handhabung von Abständen mithilfe von SCSS-Variablen. In diesem Artikel schauen wir uns die &lt;strong>SCSS-Spacer&lt;/strong> in Bootstrap an, wie sie funktionieren und wie du sie effektiv einsetzen kannst.&lt;/p>
&lt;h2 id="was-sind-scss-spacer">Was sind SCSS-Spacer?
&lt;/h2>&lt;p>Spacer in Bootstrap sind vordefinierte Werte für Margins (&lt;code>m-*&lt;/code>-Klassen) und Paddings (&lt;code>p-*&lt;/code>-Klassen). Diese Werte basieren auf der Variable &lt;code>$spacer&lt;/code> und ermöglichen ein konsistentes Spacing-System im gesamten Projekt.&lt;/p>
&lt;p>So definiert Bootstrap den Basis-Spacer:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$spacer&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">rem&lt;/span> &lt;span class="k">!default&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Standard-Spacer (bei 16px Root-Font-Size also 16px)
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Alle weiteren Abstufungen leitet Bootstrap daraus in der &lt;code>$spacers&lt;/code>-Map ab. Eigene Variablen
wie &lt;code>$spacer-lg&lt;/code> oder &lt;code>$spacer-sm&lt;/code> gibt es in Bootstrap &lt;strong>nicht&lt;/strong> – man arbeitet direkt mit
&lt;code>$spacer&lt;/code> (bzw. Vielfachen davon) und der Map.&lt;/p>
&lt;h2 id="bootstraps-spacers-map">Bootstraps &lt;code>$spacers&lt;/code>-Map
&lt;/h2>&lt;p>Bootstrap nutzt außerdem eine SCSS-Map, um Spacing-Werte dynamisch zu definieren. So sieht das aus:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$spacers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">0&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">1&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nv">$spacer&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mf">.25&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="c1">// 0.25rem (4px)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">2&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nv">$spacer&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mf">.5&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="c1">// 0.5rem (8px)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">3&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nv">$spacer&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="c1">// 1rem (16px)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">4&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nv">$spacer&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="mf">.5&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="c1">// 1.5rem (24px)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">5&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nv">$spacer&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="c1">// 3rem (48px)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="k">!default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Diese Map ermöglicht es Bootstrap, Utility-Klassen zu erzeugen wie:&lt;/p>
&lt;ul>
&lt;li>&lt;code>.m-1&lt;/code> → &lt;code>margin: .25rem;&lt;/code> (4px)&lt;/li>
&lt;li>&lt;code>.p-3&lt;/code> → &lt;code>padding: 1rem;&lt;/code> (16px)&lt;/li>
&lt;li>&lt;code>.m-5&lt;/code> → &lt;code>margin: 3rem;&lt;/code> (48px)&lt;/li>
&lt;/ul>
&lt;h2 id="wie-man-bootstrap-spacer-in-scss-verwendet">Wie man Bootstrap-Spacer in SCSS verwendet
&lt;/h2>&lt;h3 id="1-spacer">1. &lt;strong>&lt;code>$spacer&lt;/code> direkt verwenden&lt;/strong>
&lt;/h3>&lt;p>Du kannst den &lt;code>$spacer&lt;/code> (bzw. Vielfache davon) direkt in deinen Styles verwenden:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="nc">.my-section&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nv">$spacer&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 2rem (32px)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="na">margin-bottom&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nv">$spacer&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mf">.5&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 0.5rem (8px)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-map-get">2. &lt;strong>&lt;code>map-get&lt;/code> verwenden, um Spacing-Werte abzurufen&lt;/strong>
&lt;/h3>&lt;p>Um Werte dynamisch aus der &lt;code>$spacers&lt;/code>-Map zu holen, nutzt man &lt;code>map-get&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="nc">.my-box&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nf">map-get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$spacers&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Entspricht $spacer (16px)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="3-dynamische-spacing-klassen-generieren">3. &lt;strong>Dynamische Spacing-Klassen generieren&lt;/strong>
&lt;/h3>&lt;p>Du kannst eine Schleife erstellen, um eigene Spacing-Utility-Klassen zu generieren:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="k">@each&lt;/span> &lt;span class="nv">$key&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nv">$value&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nv">$spacers&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">.custom-m-&lt;/span>&lt;span class="si">#{&lt;/span>&lt;span class="nv">$key&lt;/span>&lt;span class="si">}&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">margin&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nv">$value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">.custom-p-&lt;/span>&lt;span class="si">#{&lt;/span>&lt;span class="nv">$key&lt;/span>&lt;span class="si">}&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nv">$value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Das erzeugt:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">custom-m-1&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">margin&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.25&lt;/span>&lt;span class="kt">rem&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">custom-p-3&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">rem&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="warum-scss-spacer-verwenden">Warum SCSS-Spacer verwenden?
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Konsistenz&lt;/strong> – Sorgt für einheitliches Spacing im gesamten Projekt.&lt;/li>
&lt;li>&lt;strong>Anpassbarkeit&lt;/strong> – Du kannst die &lt;code>$spacer&lt;/code>-Werte an dein Designsystem anpassen.&lt;/li>
&lt;li>&lt;strong>Effizienz&lt;/strong> – Reduziert repetitiven Code und verbessert die Wartbarkeit.&lt;/li>
&lt;/ul>
&lt;h2 id="fazit">Fazit
&lt;/h2>&lt;p>SCSS-Spacer in Bootstrap bieten eine leistungsstarke Möglichkeit, Abstände effizient zu verwalten. Durch die Verwendung vordefinierter Variablen und Maps kannst du ein konsistentes Design beibehalten und gleichzeitig dein CSS sauber und skalierbar halten. Egal ob direkt in SCSS oder über Bootstraps Utility-Klassen genutzt – Spacer helfen dabei, deinen Layout-Workflow zu vereinfachen.&lt;/p>
&lt;hr>
&lt;p>Brauchst du ein individuelleres Spacing-System? Passe die &lt;code>$spacer&lt;/code>-Variable an die Anforderungen deines Projekts an und erstelle deine eigenen Spacing-Utilities! 🚀&lt;/p></description></item><item><title>readme.so</title><link>https://besserentwickeln.de/p/readme.so/</link><pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/readme.so/</guid><description>&lt;p>&lt;strong>README-Erstellung vereinfachen mit readme.so&lt;/strong>&lt;/p>
&lt;p>Eine gut strukturierte und informative README-Datei zu erstellen ist für jedes Projekt essenziell, egal ob es sich um ein Open-Source-Repository, ein persönliches Projekt oder eine Team-Zusammenarbeit handelt. Ein README manuell zu formatieren und zu strukturieren kann jedoch zeitaufwendig und herausfordernd sein, besonders für alle, die mit der Markdown-Syntax nicht vertraut sind. Hier kommt &lt;strong>readme.so&lt;/strong> ins Spiel — ein einfaches, aber leistungsstarkes Tool, das die README-Erstellung schnell und einfach macht.&lt;/p>
&lt;h3 id="was-ist-readmeso">Was ist readme.so?
&lt;/h3>&lt;p>&lt;strong>readme.so&lt;/strong> ist ein intuitiver, webbasierter Editor, mit dem README-Dateien mühelos erstellt und bearbeitet werden können. Er bietet eine übersichtliche Oberfläche mit vordefinierten Abschnitten, die je nach Bedarf hinzugefügt oder entfernt werden können. Das Tool ist besonders nützlich für Entwickler und Projektbetreuer, die ein professionell aussehendes README erstellen möchten, ohne übermäßig viel Zeit mit der Formatierung zu verbringen.&lt;/p>
&lt;h3 id="hauptfunktionen">Hauptfunktionen
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Drag-and-Drop-Oberfläche&lt;/strong>: Abschnitte wie Installation, Nutzung, Contributing, Lizenz und mehr einfach hinzufügen, umsortieren oder entfernen.&lt;/li>
&lt;li>&lt;strong>Markdown-Unterstützung&lt;/strong>: Inhalte direkt in Markdown mit Echtzeit-Vorschau bearbeiten.&lt;/li>
&lt;li>&lt;strong>Vorgefertigte Vorlagen&lt;/strong>: Schnell mit gängigen README-Strukturen starten.&lt;/li>
&lt;li>&lt;strong>Anpassung&lt;/strong>: Überschriften, Beschreibungen und Anweisungen an die Bedürfnisse des Projekts anpassen.&lt;/li>
&lt;li>&lt;strong>Export mit einem Klick&lt;/strong>: Die README-Datei sofort im Markdown-Format herunterladen.&lt;/li>
&lt;/ul>
&lt;h3 id="warum-readmeso-verwenden">Warum readme.so verwenden?
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>Zeitersparnis&lt;/strong> – Statt eine README-Datei manuell zu strukturieren und zu formatieren, können die vorgefertigten Abschnitte genutzt werden, um schnell Inhalte zu erzeugen.&lt;/li>
&lt;li>&lt;strong>Anfängerfreundlich&lt;/strong> – Es sind keine Markdown-Vorkenntnisse nötig, wodurch das Tool für alle zugänglich ist.&lt;/li>
&lt;li>&lt;strong>Bessere Dokumentation&lt;/strong> – Eine gut strukturierte README verbessert die Nutzererfahrung und hilft anderen, das Projekt besser zu verstehen.&lt;/li>
&lt;li>&lt;strong>Kostenlos&lt;/strong> – Das Tool ist völlig kostenlos und erfordert keine Anmeldung.&lt;/li>
&lt;/ol>
&lt;h3 id="erste-schritte">Erste Schritte
&lt;/h3>&lt;ol>
&lt;li>&lt;a class="link" href="https://readme.so/" target="_blank" rel="noopener"
>readme.so&lt;/a> besuchen.&lt;/li>
&lt;li>Abschnitte für das README auswählen.&lt;/li>
&lt;li>Die Inhalte direkt im Editor bearbeiten.&lt;/li>
&lt;li>Die Änderungen in Echtzeit als Vorschau ansehen.&lt;/li>
&lt;li>Die README-Datei exportieren und speichern.&lt;/li>
&lt;/ol>
&lt;h3 id="fazit">Fazit
&lt;/h3>&lt;p>Ein gut geschriebenes README ist für jedes Projekt entscheidend, und &lt;strong>readme.so&lt;/strong> macht diesen Prozess einfacher als je zuvor. Egal ob erfahrener Entwickler oder Anfänger — dieses Tool hilft dabei, in wenigen Minuten eine professionelle, gut strukturierte Dokumentation zu erstellen. Einfach mal ausprobieren und die Projektdokumentation optimieren!&lt;/p>
&lt;p>Hast du readme.so schon einmal genutzt? Teile deine Erfahrung in den Kommentaren!&lt;/p></description></item><item><title>Dateifreigabe lokal via Python</title><link>https://besserentwickeln.de/p/dateifreigabe-lokal-via-python/</link><pubDate>Fri, 21 Feb 2025 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/dateifreigabe-lokal-via-python/</guid><description>&lt;h1 id="dateien-lokal-via-python-teilen-kurze-und-einfache-anleitung">Dateien lokal via Python teilen: Kurze und einfache Anleitung
&lt;/h1>&lt;h2 id="warum-python-für-lokale-dateifreigabe-nutzen">Warum Python für lokale Dateifreigabe nutzen?
&lt;/h2>&lt;p>Pythons integrierter HTTP-Server bietet eine einfache und effiziente Möglichkeit, Dateien innerhalb eines lokalen Netzwerks zu teilen. Es wird keine zusätzliche Software benötigt, was ihn zu einer schlanken und effektiven Lösung für schnelle Dateiübertragungen macht.&lt;/p>
&lt;h2 id="voraussetzungen-python-auf-deinem-system-installieren">Voraussetzungen: Python auf deinem System installieren
&lt;/h2>&lt;p>Vor dem Fortfahren sicherstellen, dass Python installiert ist:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo apt update &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> sudo apt install -y python3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="wie-man-die-lokale-ip-adresse-findet">Wie man die lokale IP-Adresse findet
&lt;/h2>&lt;p>Um die lokale IP-Adresse herauszufinden, nutze:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hostname -I &lt;span class="p">|&lt;/span> awk &lt;span class="s1">&amp;#39;{print $1}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Deine lokale IP sollte wie &lt;code>192.168.xxx.xxx&lt;/code> aussehen. Das ist wichtig, um von einem anderen Gerät auf die geteilten Dateien zugreifen zu können.&lt;/p>
&lt;h2 id="schritt-für-schritt-den-python-http-server-starten">Schritt für Schritt: Den Python-HTTP-Server starten
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Ein Terminal öffnen und in das Verzeichnis mit den Dateien wechseln, die geteilt werden sollen:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> /path/to/your/files
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>Den Python-HTTP-Server starten:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">python3 -m http.server &lt;span class="m">8000&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Das startet einen einfachen HTTP-Server auf Port &lt;code>8000&lt;/code>, wodurch die Dateien im Verzeichnis über das lokale Netzwerk erreichbar werden.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="wie-man-von-einem-anderen-gerät-auf-die-geteilten-dateien-zugreift">Wie man von einem anderen Gerät auf die geteilten Dateien zugreift
&lt;/h2>&lt;p>Auf jedem Gerät im selben Netzwerk:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Einen Webbrowser öffnen.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Folgendes in die Adresszeile eingeben:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">http://192.168.xxx.xxx:8000
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;em>(&lt;code>xxx.xxx&lt;/code> durch die tatsächliche lokale IP-Adresse ersetzen.)&lt;/em>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="best-practices-für-sichere-lokale-dateifreigabe">Best Practices für sichere lokale Dateifreigabe
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>&lt;strong>Lokal bleiben&lt;/strong>: Diese Methode ist nur für lokale Netzwerke gedacht. &lt;strong>Diesen Server niemals dem Internet aussetzen.&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Port bei Bedarf ändern&lt;/strong>: Falls Port &lt;code>8000&lt;/code> belegt ist, einen anderen wählen:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">python3 -m http.server &lt;span class="m">8080&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Interface gezielt wählen&lt;/strong>: &lt;code>--bind&lt;/code> bestimmt, auf welcher Netzwerk-Schnittstelle der
Server lauscht – z. B. nur im LAN und nicht über ein zusätzliches VPN:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">python3 -m http.server &lt;span class="m">8000&lt;/span> --bind 192.168.xxx.xxx
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Das begrenzt aber &lt;strong>nicht&lt;/strong>, &lt;em>wer&lt;/em> sich verbinden darf – jeder, der die Adresse erreicht,
hat Zugriff. Für echte Zugriffsbeschränkung eine Firewall verwenden.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="fazit">Fazit
&lt;/h2>&lt;p>Pythons integrierter HTTP-Server ist eine schnelle und effektive Möglichkeit, Dateien lokal zu teilen. Mit diesen Schritten kannst du Daten einfach zwischen Geräten übertragen, ohne zusätzliche Software. &lt;strong>Speichere diese Anleitung für später!&lt;/strong>&lt;/p></description></item><item><title>SCSS - Padding und Margin verschachteln</title><link>https://besserentwickeln.de/p/scss-padding-und-margin-verschachteln/</link><pubDate>Mon, 03 Feb 2025 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/scss-padding-und-margin-verschachteln/</guid><description>&lt;h1 id="optimiertes-scss-nesting-für-sauberen-und-effizienten-code">Optimiertes SCSS-Nesting für sauberen und effizienten Code
&lt;/h1>&lt;p>Lerne, wie man SCSS-Nesting nutzt, um sauberere und besser strukturierte Styles zu schreiben. Der Standardansatz hält Eigenschaften
organisiert, während Nesting hilft, die Übersichtlichkeit zu bewahren, indem verwandte Styles gruppiert werden. Verbessere Lesbarkeit und Wartbarkeit mit
dieser einfachen SCSS-Technik! 🚀&lt;/p>
&lt;h2 id="standard">Standard
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="nc">.class&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="no">black&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding-top&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="mf">.625&lt;/span>&lt;span class="kt">rem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding-bottom&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="mf">.875&lt;/span>&lt;span class="kt">rem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="verschachtelt">Verschachtelt
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="nc">.class&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="no">black&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">top&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="mf">.625&lt;/span>&lt;span class="kt">rem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">bottom&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="mf">.875&lt;/span>&lt;span class="kt">rem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Beide Varianten kompilieren zum selben CSS (&lt;code>padding-top&lt;/code> / &lt;code>padding-bottom&lt;/code>). Die
verschachtelte Schreibweise ist Sass-spezifisch (&amp;ldquo;nested properties&amp;rdquo;) und funktioniert
weiterhin in Dart Sass.&lt;/p></description></item><item><title>ImageMagick Batch-Resize</title><link>https://besserentwickeln.de/p/imagemagick-batch-resize/</link><pubDate>Mon, 19 Dec 2022 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/imagemagick-batch-resize/</guid><description>&lt;h2 id="voraussetzung">Voraussetzung
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="http://www.imagemagick.org/script/index.php" target="_blank" rel="noopener"
>ImageMagick&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="installation">Installation
&lt;/h3>&lt;h4 id="debianubuntu">Debian/Ubuntu
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo apt-get install imagemagick
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="macosbrew">MacOS/Brew
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">brew install imagemagick
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="lösung">Lösung
&lt;/h2>&lt;p>Dies in dem Verzeichnis mit den Bildern ausführen, die verkleinert werden sollen. Nicht vergessen, vorher ein Verzeichnis &lt;code>small-jpgs&lt;/code> zu erstellen.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mogrify -path ../small-jpgs -resize 30% *
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Es ist auch möglich, die Qualität zu reduzieren, indem man z. B. &lt;code>-quality 90&lt;/code> hinzufügt.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mogrify -path ../small-jpgs -quality &lt;span class="m">90&lt;/span> -resize 30% *
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Und wenn gewünscht, kann sogar das Bildformat mit &lt;code>-format jpg&lt;/code> geändert werden.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mogrify -path ../small-jpgs -quality &lt;span class="m">90&lt;/span> -format jpg -resize 30% *
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>M1 DDEV Chromium</title><link>https://besserentwickeln.de/p/m1-ddev-chromium/</link><pubDate>Tue, 29 Nov 2022 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/m1-ddev-chromium/</guid><description>&lt;p>Einer meiner Kollegen bekam auf seinem M1-Macbook beim Ausführen von &lt;code>bin/build-storefront.sh&lt;/code> folgenden Fehler.&lt;/p>
&lt;h2 id="fehlermeldung">Fehlermeldung
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even &lt;span class="k">if&lt;/span> nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! code &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! path /var/www/html/vendor/shopware/storefront/Resources/app/storefront/node_modules/puppeteer
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! &lt;span class="nb">command&lt;/span> failed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! &lt;span class="nb">command&lt;/span> sh /tmp/install-9c2094bf.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! The chromium binary is not available &lt;span class="k">for&lt;/span> arm64.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! If you are on Ubuntu, you can install with:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! sudo apt install chromium
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! sudo apt install chromium-browser
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! /var/www/html/vendor/shopware/storefront/Resources/app/storefront/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserFetcher.js:115
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! throw new Error&lt;span class="o">()&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! ^
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! Error
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! at /var/www/html/vendor/shopware/storefront/Resources/app/storefront/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserFetcher.js:115:27
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! at FSReqCallback.oncomplete &lt;span class="o">(&lt;/span>node:fs:206:21&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Als Workaround haben wir &lt;code>--ignore-scripts --legacy-peer-deps&lt;/code> zu jedem &lt;code>npm install&lt;/code> bzw. &lt;code>npm clean-install&lt;/code> in der &lt;code>build-storefront.sh&lt;/code> hinzugefügt.&lt;/p></description></item><item><title>Shopware 6: Automatisiertes Security-Update mit GitLab</title><link>https://besserentwickeln.de/p/shopware-6-automatisiertes-security-update-mit-gitlab/</link><pubDate>Thu, 25 Aug 2022 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/shopware-6-automatisiertes-security-update-mit-gitlab/</guid><description>&lt;p>Diese Pipeline erstellt automatisch einen Merge Request mit einem Update des Shopware Security Plugins, basierend auf einem Tag.&lt;/p>
&lt;h2 id="voraussetzungen">Voraussetzungen
&lt;/h2>&lt;ul>
&lt;li>GitLab&lt;/li>
&lt;li>Shopware 6 Composer-Setup&lt;/li>
&lt;/ul>
&lt;h3 id="variablen">Variablen
&lt;/h3>&lt;ul>
&lt;li>SSH_DEPLOY_PRIVATE_KEY
&lt;ul>
&lt;li>Der private Schlüssel eines erstellten Schlüsselpaars muss in dieser Variable gespeichert werden&lt;/li>
&lt;li>Der öffentliche Schlüssel muss unter „Settings &amp;gt; Repository &amp;gt; Deploy Key“ eingetragen werden
&lt;ul>
&lt;li>Nicht vergessen, Schreibrechte zu vergeben&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>SSH_KNOWN_HOSTS
&lt;ul>
&lt;li>Diese aus der &lt;a class="link" href="https://docs.gitlab.com/ee/user/gitlab_com/#ssh-known_hosts-entries" target="_blank" rel="noopener"
>GitLab-Dokumentation&lt;/a> entnehmen&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="code">Code
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">stages&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">security&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">securityUpdate&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">stage&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">security&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rules&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="k">if&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;$CI_COMMIT_TAG =~ /security-.*/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">image&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;kellerkinder/pipeline-image:8.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">before_script&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">eval&lt;/span> &lt;span class="o">$&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ssh&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">agent&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">echo&lt;/span> &lt;span class="s2">&amp;#34;${SSH_DEPLOY_PRIVATE_KEY}&amp;#34;&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">tr&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">d&lt;/span> &lt;span class="s1">&amp;#39;&lt;/span>&lt;span class="se">\r&lt;/span>&lt;span class="s1">&amp;#39;&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">ssh&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">add&lt;/span> &lt;span class="o">-&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">mkdir&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">~/.&lt;/span>&lt;span class="n">ssh&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">~/.&lt;/span>&lt;span class="n">ssh&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">known_hosts&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">echo&lt;/span> &lt;span class="s2">&amp;#34;$SSH_KNOWN_HOSTS&amp;#34;&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="o">~/.&lt;/span>&lt;span class="n">ssh&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">known_hosts&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">chmod&lt;/span> &lt;span class="mi">700&lt;/span> &lt;span class="o">~/.&lt;/span>&lt;span class="n">ssh&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">git&lt;/span> &lt;span class="n">config&lt;/span> &lt;span class="n">user&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="s2">&amp;#34;security&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">git&lt;/span> &lt;span class="n">config&lt;/span> &lt;span class="n">user&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">email&lt;/span> &lt;span class="s2">&amp;#34;ci@example&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">git&lt;/span> &lt;span class="n">remote&lt;/span> &lt;span class="n">remove&lt;/span> &lt;span class="n">origin&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="bp">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">git&lt;/span> &lt;span class="n">remote&lt;/span> &lt;span class="n">add&lt;/span> &lt;span class="n">origin&lt;/span> &lt;span class="s2">&amp;#34;git@gitlab.com:path/repo.git&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">script&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">cd&lt;/span> &lt;span class="o">$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">CI_PROJECT_DIR&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">composer&lt;/span> &lt;span class="n">install&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">no&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">interaction&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">optimize&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">autoloader&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">composer&lt;/span> &lt;span class="n">update&lt;/span> &lt;span class="n">store&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">shopware&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">swagplatformsecurity&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">git&lt;/span> &lt;span class="n">checkout&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="n">feature&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">security&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">patch&lt;/span>&lt;span class="o">-$&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span> &lt;span class="o">+%&lt;/span>&lt;span class="n">F&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">git&lt;/span> &lt;span class="n">add&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">A&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="o">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">git&lt;/span> &lt;span class="n">diff&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">cached&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">quiet&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">echo&lt;/span> &lt;span class="s2">&amp;#34;Kein Security-Update verfügbar – nichts zu committen.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exit&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">git&lt;/span> &lt;span class="n">commit&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">m&lt;/span> &lt;span class="s2">&amp;#34;Security Update $(date +&lt;/span>&lt;span class="si">%F&lt;/span>&lt;span class="s2">)&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span> &lt;span class="n">git&lt;/span> &lt;span class="n">push&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">set&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">upstream&lt;/span> &lt;span class="n">origin&lt;/span> &lt;span class="n">feature&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">security&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">patch&lt;/span>&lt;span class="o">-$&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span> &lt;span class="o">+%&lt;/span>&lt;span class="n">F&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="beispiel-tag">Beispiel-Tag
&lt;/h2>&lt;p>&lt;code>security-YYMMDD&lt;/code>&lt;/p></description></item><item><title>History-Format</title><link>https://besserentwickeln.de/p/history-format/</link><pubDate>Wed, 08 Jun 2022 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/history-format/</guid><description>&lt;p>Der History einen Zeitstempel für den Befehlsverlauf hinzufügen (nur für die aktuelle Terminal-Sitzung) mit folgendem Befehl:&lt;/p>
&lt;p>&lt;code>HISTTIMEFORMAT=&amp;quot;%F %T &amp;quot;&lt;/code>&lt;/p>
&lt;p>Jetzt &lt;code>history&lt;/code> eingeben, und du solltest Zeitstempel bei deinen Bash-History-Befehlen sehen.&lt;/p>
&lt;p>&lt;a class="link" href="https://www.linuxuprising.com/2019/07/bash-history-how-to-show-timestamp-when.html" target="_blank" rel="noopener"
>Quelle&lt;/a>&lt;/p></description></item><item><title>MySQL: ANY command denied to user</title><link>https://besserentwickeln.de/p/mysql-any-command-denied-to-user/</link><pubDate>Wed, 08 Jun 2022 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/mysql-any-command-denied-to-user/</guid><description>&lt;h1 id="mysql-any-command-denied-to-user">MySQL ANY command denied to user
&lt;/h1>&lt;h2 id="fehler">Fehler
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">ERROR &lt;span class="m">1142&lt;/span> &lt;span class="o">(&lt;/span>42000&lt;span class="o">)&lt;/span> at line 11215: ANY &lt;span class="nb">command&lt;/span> denied to user &lt;span class="s1">&amp;#39;db-user-exported&amp;#39;&lt;/span>@&lt;span class="s1">&amp;#39;%&amp;#39;&lt;/span> &lt;span class="k">for&lt;/span> table &lt;span class="s1">&amp;#39;dummy&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="lösung">Lösung
&lt;/h2>&lt;h3 id="eine-kopie-erstellen">Eine Kopie erstellen
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">cp dev-dump.sql dev-dump-copy.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="alten-benutzer-und-alte-db-ersetzen">Alten Benutzer und alte DB ersetzen
&lt;/h3>&lt;p>Im Dump steckt der &lt;code>DEFINER&lt;/code> des Quellsystems (im Fehler oben &lt;code>db-user-exported&lt;/code>). Dieser wird
auf den lokalen Benutzer bzw. die lokale DB umgeschrieben:&lt;/p>
&lt;p>&lt;strong>Alter Benutzer (im Dump):&lt;/strong> &lt;code>db-user-exported&lt;/code> → &lt;strong>Neuer Benutzer:&lt;/strong> &lt;code>db-user-target&lt;/code>&lt;/p>
&lt;p>&lt;strong>Alte DB (im Dump):&lt;/strong> &lt;code>db-exported&lt;/code> → &lt;strong>Neue DB:&lt;/strong> &lt;code>db-target&lt;/code>&lt;/p>
&lt;p>Zuerst den spezifischeren Benutzernamen ersetzen, dann den DB-Namen:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/db-user-exported/db-user-target/g&amp;#39;&lt;/span> dev-dump-copy.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/db-exported/db-target/g&amp;#39;&lt;/span> dev-dump-copy.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="reimport">Reimport
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mysql -u db-user-target -p db-target &amp;lt; dev-dump-copy.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="alternative-nur-routinen-und-trigger-nachziehen">Alternative: nur Routinen und Trigger nachziehen
&lt;/h2>&lt;p>Wenn nur Stored Procedures, Functions und Trigger am Definer scheitern, importiert man den
Rest normal (z. B. mit &lt;code>--force&lt;/code>) und zieht diese Objekte separat aus der Quell-DB nach.&lt;/p>
&lt;p>Aus der Quell-DB exportieren:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mysqldump --no-data --no-create-info --routines --triggers db-exported &amp;gt; routines-triggers.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Definer ersetzen:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/db-user-exported/db-user-target/g&amp;#39;&lt;/span> routines-triggers.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sed -i &lt;span class="s1">&amp;#39;s/db-exported/db-target/g&amp;#39;&lt;/span> routines-triggers.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In die Ziel-DB einspielen:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mysql -u db-user-target -p db-target &amp;lt; routines-triggers.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>Nur die drei neuesten Dateien behalten</title><link>https://besserentwickeln.de/p/nur-die-drei-neuesten-dateien-behalten/</link><pubDate>Tue, 24 May 2022 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/nur-die-drei-neuesten-dateien-behalten/</guid><description>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">ls -t | sed -e &amp;#39;1,3d&amp;#39; | xargs -d &amp;#39;\n&amp;#39; -r rm
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="was-passiert-hier">Was passiert hier?
&lt;/h2>&lt;p>&lt;code>ls -t&lt;/code>
listet alle Dateien im aktuellen Verzeichnis in absteigender Reihenfolge der Änderungszeit auf. Die zuletzt geänderten Dateien stehen zuerst, ein Dateiname pro Zeile.&lt;/p>
&lt;p>&lt;code>sed -e '1,3d'&lt;/code> löscht die ersten 3 Zeilen, also die 3 neuesten Dateien.&lt;/p>
&lt;p>&lt;code>xargs -d '\n' -r rm&lt;/code> liest die restlichen Zeilen, nimmt dabei &lt;code>\n&lt;/code> als einziges Trennzeichen
(so überstehen Dateinamen mit Leerzeichen) und hängt sie als Argumente an einen – bei sehr
vielen Dateien auch mehrere – &lt;code>rm&lt;/code>-Aufrufe an. &lt;code>-r&lt;/code> verhindert, dass &lt;code>rm&lt;/code> ohne Argumente
läuft, falls es 3 oder weniger Dateien gibt.&lt;/p>
&lt;blockquote>
&lt;p>&lt;code>xargs -d&lt;/code> ist eine GNU-Erweiterung. Unter macOS/BSD stattdessen
&lt;code>ls -t | tail -n +4 | tr '\n' '\0' | xargs -0 rm&lt;/code> verwenden.&lt;/p>&lt;/blockquote>
&lt;p>&lt;a class="link" href="https://stackoverflow.com/questions/2960022/shell-script-to-count-files-then-remove-oldest-files" target="_blank" rel="noopener"
>Quelle&lt;/a>&lt;/p></description></item><item><title>Ubuntu Standard-Audiogerät</title><link>https://besserentwickeln.de/p/ubuntu-standard-audioger%C3%A4t/</link><pubDate>Thu, 15 Apr 2021 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/ubuntu-standard-audioger%C3%A4t/</guid><description>&lt;p>Ich habe ein wenig nach der Antwort gesucht und Folgendes gefunden: &lt;a class="link" href="https://itectec.com/ubuntu/ubuntu-how-do-you-set-a-default-audio-output-device-in-ubuntu-18-04/" target="_blank" rel="noopener"
>https://itectec.com/ubuntu/ubuntu-how-do-you-set-a-default-audio-output-device-in-ubuntu-18-04/&lt;/a>&lt;/p>
&lt;p>Ich speichere das hier ab, weil ich es garantiert irgendwann wieder suchen werde.&lt;/p>
&lt;ol>
&lt;li>Ausführen: &lt;code>pactl list short sinks&lt;/code>&lt;/li>
&lt;li>Den Gerätenamen notieren, den man als Standard verwenden möchte&lt;/li>
&lt;li>Ausführen: &lt;code>pactl set-default-sink &amp;lt;Your_Device_Name&amp;gt;&lt;/code>
Das sollte ohne Fehlermeldung funktionieren.&lt;/li>
&lt;li>Die Anwendung „Startprogramme“ öffnen (sollte auf Ubuntu vorinstalliert sein)&lt;/li>
&lt;li>Auf „Hinzufügen“ klicken&lt;/li>
&lt;li>Dem Autostart-Eintrag einen Namen geben&lt;/li>
&lt;li>Den Befehl von oben in das Befehlsfeld kopieren:
&lt;code>pactl set-default-sink 'Your-Device-Name'&lt;/code>&lt;/li>
&lt;li>Auf „Speichern“ klicken.&lt;/li>
&lt;/ol></description></item><item><title>Array-Methoden zum Hinzufügen und Entfernen von Elementen</title><link>https://besserentwickeln.de/p/einige-es6-funktionen/</link><pubDate>Sat, 21 Mar 2020 00:00:00 +0000</pubDate><guid>https://besserentwickeln.de/p/einige-es6-funktionen/</guid><description>&lt;p>Alle folgenden Methoden verändern das Array direkt (sie mutieren es):&lt;/p>
&lt;p>&lt;code>.pop()&lt;/code> entfernt das &lt;strong>letzte&lt;/strong> Element eines Arrays und gibt es zurück.
&lt;code>.shift()&lt;/code> entfernt das &lt;strong>erste&lt;/strong> Element eines Arrays und gibt es zurück.&lt;/p>
&lt;p>&lt;code>.push()&lt;/code> fügt ein Element am &lt;strong>Ende&lt;/strong> des Arrays hinzu und gibt die neue Länge zurück.
&lt;code>.unshift()&lt;/code> fügt ein Element am &lt;strong>Anfang&lt;/strong> des Arrays hinzu und gibt die neue Länge zurück.&lt;/p>
&lt;p>&lt;code>.splice(1, 3)&lt;/code> &lt;strong>entfernt&lt;/strong> ab Index 1 drei Elemente aus dem Array und gibt die entfernten
Elemente als neues Array zurück. Nicht zu verwechseln mit &lt;code>.slice(1, 3)&lt;/code>, das das Array
unverändert lässt und nur eine Kopie des Ausschnitts (Index 1 bis 2) liefert.&lt;/p>
&lt;p>Diese Methoden gehören übrigens nicht zu ES6 – &lt;code>.pop()&lt;/code>, &lt;code>.shift()&lt;/code>, &lt;code>.push()&lt;/code>, &lt;code>.unshift()&lt;/code>
und &lt;code>.splice()&lt;/code> sind schon seit ES3 (1999) Teil von JavaScript.&lt;/p></description></item></channel></rss>