+
+
+{% markdown %}
+### String Split and Join
+
+This challenge involves a string containing spaces, where the spaces are to be
+replaced with dashes (`-`) instead.
+{% endmarkdown %}
+
+
+
+
+
+
+{% highlight python3 tio=https://tio.run/##K6gsycjPM/7/PyU1TaG4ICezJD4xLyU@Kz8zTyMnMy9V04pLAQiKUktKi/IUlHSV9OBSemDlGkoKSpqa/wuKMvNKNNAMyMwrKC3R0ARKp@XnKyQlFgFxFQA %}
+def split_and_join(line):
+ return "-".join(line.split(" "))
+{% endhighlight %}
+
+
+
+
+
+
+
+{% highlight perl6 tio=https://tio.run/##K0gtyjH7/7@4NEmhuCAns0Q3MS9FNys/M09BQyUnMy9VU6GaSwEIwBw9sBINJQUlTT2QGg0lXSVNrtr/KlqefnrFOaVFBXpqqKboFSdW/k/Lz1dISiwC4ioA %}
+sub split-and-join ($line) {
+ $line.split(" ").join("-")
+}
+{% endhighlight %}
+
+
+
+
+
+
+
+{% markdown %}
+### What's Your Name?
+
+The next challenge is a simply string formatting task. You get two inputs, a
+first name and a last name, and have to put it in a string which will be
+printed to `STDOUT`.
+{% endmarkdown %}
+
+
+
+
+
+
+{% highlight python3 tio=https://tio.run/##XYzBCsIwEETv/YqxUEigePHmD@jZm6cSyRYj22ww2UK/PobqyWEOAzPz0laeEk@1epqR3iGWaVbmKbqFjBvxsOcOTXtl@isxC4bcfMBdFC/NBZ54JY@2EKQdeOwx4Pu39R8bYtJi7IhfsPWiwUu3uoib5KzLBw %}
+def print_full_name(a, b):
+ print("Hello %s %s! You just delved into python." % (a, b))
+{% endhighlight %}
+
+
+
+
+
+
+
+{% highlight perl6 tio=https://tio.run/##K0gtyjH7/7@4NEmhoCgzr0Q3rTQnRzcvMTdVQyVRR0ElSVOhmksBCIoTKxWUPFJzcvIVVBKB4ooKkfmlClmlxSUKKak5ZakpCkDd@QoBQPMUzPSUuGr/Y5in5emnl55aAjQVytL875NYVFTJFZ6YkwMA %}
+sub print-full-name($a, $b) {
+ say "Hello $a $b! You just delved into Perl 6."
+}
+{% endhighlight %}
+
+
+
+
+
+
+
+{% markdown %}
+### Mutations
+
+You are given a string _string_, an integer _position_ and a character
+_character_. In _string_, replace the character at _position_ to the given
+_character_. The position is counted from starting point 0, so I don't have to
+think about differences between what a human or computer considers to be
+position 1 in a string.
+{% endmarkdown %}
+
+
+
+
+
+
+{% highlight python3 tio=https://tio.run/##VY5BC8IwDIXv/RVhpxaKIN4Ef4mIjNm5iCYlTQ/79bXrpmIuyeN975E468R0KOUWRnhl7TVckwrS3a7LQ@SEikwehqmXftAg7migzqITnOCJSTfc/YzzJ3ipyDdqGiBBsxB03e7BSLbxrsTaoPb/C6SY1ToPi7WJptbLlZHZ7I28AQ %}
+def mutate_string(string, position, character):
+ chars = list(string)
+ chars[position] = character
+
+ return "".join(chars)
+{% endhighlight %}
+
+
+
+
+
+
+
+{% highlight perl6 tio=https://tio.run/##K0gtyjH7/7@4NEkht7QksSRVt7ikKDMvXUFDBcLQUVApyC/OLMnMzwMykzMSixKTS1KLNBWquRSAILdSwSEns7hEwVYBqkEvOT83yRosCZaJhuuPBSmCm2DNhVCjl5WfmWfNVfsfxQ0aKlqefnrpqSVAizFYmnrFiZX/0/LzuQy5igA %}
+sub mutate-string ($string, $position, $character) {
+ my @list = $string.comb;
+ @list[$position] = $character;
+
+ @list.join;
+}
+{% endhighlight %}
+
+
+
+
+
+
+
+{% markdown %}
+### Find a String
+
+In the following challenge you are given a string _string_, and a substring
+_sub\_string_. The challenge is to find how often a substring occurs in the
+_string_. The substrings may overlap one another, so the string `"ABCDCDC"`
+contains the substring `"CDC"` twice.
+{% endmarkdown %}
+
+
+
+
+
+
+{% highlight python3 tio=https://tio.run/##XU67CsMwDNz9FTfaxENKt4CHPv4ilNCHkwqKbBx76Ne7bhxIqYSQdNzp5N/x6Xif88OOuLvEcZjTbY6BeJK1aRRkqLPqBEosRBi0YllHF0AgRrjyZGWr8bK8qtUq@QaNqGBPHaGprO32Bcb8eG26zbIx2FXPYGMKXOHsCz/K//eJfYpSaayDUvlwPJ1LilIf %}
+def count_substring(string, sub_string):
+ count = 0
+
+ for i in range(0, len(string)):
+ if string[i:i + len(sub_string)] == sub_string:
+ count += 1
+
+ return count
+{% endhighlight %}
+
+
+
+
+
+
+
+{% highlight perl6
+tio=https://tio.run/##K0gtyjH7/7@4NEkhOb80r0QXyCouKcrMS1fQUIEwdBRUgIK6EI6mQjUXZ2pOam6xAlRaoa5OIdcqvwxoUGKBPpJSfa7a/2hmaqhoefrppaeWAM2EsjT1ihMr/zs6ObsAIRcQAwA %}
+sub count-substring ($string, $sub-string) {
+ elems $string ~~ m:overlap/$sub-string/
+}
+{% endhighlight %}
+
+
+
+
+
+
+
+{% markdown %}
+### String Validators
+
+In the following challenge, the program is given a string _s_, and have to
+validate a number of properties on this string. These are, in order, whether
+they contain
+
+- alphanumeric characters (a-z, A-Z or 0-9),
+- alphabetic characters (a-z or A-Z),
+- digits (0-9),
+- lowercase characters (a-z),
+- uppercase characters (A-Z).
+
+If any character in the given string passes a validation, it must print
+`"True"`, otherwise it must print `"False"`.
+
+{% endmarkdown %}
+
+
+
+
+
+
+{% highlight python3 tio=https://tio.run/##hZLNioMwEIDvPsXQPdTAIrS9CR72sk@wt1JCVtMaqklIIqWUPrubn6p1MZib38x8zmQi76YW/ND37AwYc9JSjKEoYItxSxjHeJsnYI@GAhiXnUlR4kFZ0/Lq6MN/urMhDe/aTQ7fpNH0853Lmizwil2YWeCNuFG1wDspJ@7xMzRzFso2RJRtERqmTapRPpbZybgwr4aPryZPQHjlazKmPUrfSqYBp/wCflRHk7jWzfhPa1FcG/JXtOGKZlqPYtohf0Ubbnim9SimHfJXtGFBM61HMe2QH7QQvFd6d@/K7zEkZg6lCI3hTAs1vkS3fAvd7l1s@pNUjA@Kow2dUN9//ZbVbn/4@AM %}
+if __name__ == '__main__':
+ s = input()
+
+ checks = {
+ "alnum": False,
+ "alpha": False,
+ "digit": False,
+ "lower": False,
+ "upper": False
+ }
+
+ for char in list(s):
+ if not checks["alnum"] and char.isalnum():
+ checks["alnum"] = True
+
+ if not checks["alpha"] and char.isalpha():
+ checks["alpha"] = True
+
+ if not checks["digit"] and char.isdigit():
+ checks["digit"] = True
+
+ if not checks["lower"] and char.islower():
+ checks["lower"] = True
+
+ if not checks["upper"] and char.isupper():
+ checks["upper"] = True
+
+ keys = list(checks.keys())
+ keys.sort()
+
+ for key in keys:
+ print(checks[key])
+{% endhighlight %}
+
+
+
+
+
+
+
+{% highlight perl6
+tio=https://tio.run/##K0gtyjH7/7@4NEnB19HTT6GaSwEIcisVVIoVbBVUtDz99NJTS6y5wMLFiZUKxfkgqbo6BX2bxJy80lw7fWvscgUZiTjkUjLTM0twyOXkl6cW4ZArLSiAyNX@/@@YlJxiaGSsDAA %}
+sub MAIN {
+ my $s = $*IN.get;
+
+ say so $s ~~ /
/;
+ say so $s ~~ //;
+ say so $s ~~ //;
+ say so $s ~~ //;
+ say so $s ~~ //;
+}
+{% endhighlight %}
+
+
+
+
+
+
+
+{% markdown %}
+### Text Wrap
+
+You are given a string _s_ and a width _w_. The string should be split over
+multiple lines so it is never more wide than _w_.
+{% endmarkdown %}
+
+
+
+
+
+
+{% highlight python3 tio=https://tio.run/##K6gsycjPM/7/PzO3IL@oRKEktaKkvCixgIsrJTVNAcTSKC4pysxL11HITayIL89MKcnQtOJSAIKi1JLSojwFpZg8Jb2s/Mw8DZhePRzaNP8XAIVKNMDSmXkFpSUamjoKIBEoBwj@Ozo5u7i6uXt4enn7@Pr5BwQGBYeEhoVHREZxmQIA %}
+import textwrap
+
+def wrap(string, max_width):
+ return "\n".join(textwrap.wrap(string, max_width))
+{% endhighlight %}
+
+
+
+
+
+
+
+{% highlight perl6 tio=https://tio.run/##K0gtyjH7/7@4NEmhvCixQEFDpbikKDMvXUdBpTwzpSRDU6GaSwEIoMJ6yfm5SRpQKb2s/Mw8DaWYPCVNrtr/IO0aKlqefnrpqSVA7VCWnmdeiaZecWLlf0cnZxdXN3cPTy9vH18//4DAoOCQ0LDwiMgoLlMA %}
+sub wrap ($string, $width) {
+ $string.comb($width).join("\n")
+}
+{% endhighlight %}
+
+
+
+
+
+
+
+{% markdown %}
+### Designer Door Mat
+
+This challenge is more complex than previous challenges. The task at hand is to
+"draw" a certain "design" as the output. For the input, you are given both a
+height _y_ and a width _x_, however _x_ must always be _y_ × 3, so you can
+ignore the second argument.
+
+This one is much simpler to explain using two examples. The first example is
+the output if the input were `7 21`.
+{% endmarkdown %}
+
+{% highlight text %}
+---------.|.---------
+------.|..|..|.------
+---.|..|..|..|..|.---
+-------WELCOME-------
+---.|..|..|..|..|.---
+------.|..|..|.------
+---------.|.---------
+{% endhighlight %}
+
+{% markdown %}
+In the second example, the input is `11 33`.
+{% endmarkdown %}
+
+{% highlight text %}
+---------------.|.---------------
+------------.|..|..|.------------
+---------.|..|..|..|..|.---------
+------.|..|..|..|..|..|..|.------
+---.|..|..|..|..|..|..|..|..|.---
+-------------WELCOME-------------
+---.|..|..|..|..|..|..|..|..|.---
+------.|..|..|..|..|..|..|.------
+---------.|..|..|..|..|.---------
+------------.|..|..|.------------
+---------------.|.---------------
+{% endhighlight %}
+
+
+
+
+
+
+{% highlight python3 tio=https://tio.run/##zZDNS8MwGMbv@SseMwbJlrbrehCEnWS3DS@CBxGpNFsCJQ1p5xD832s@Nh2I7GpyCHmf3/u8H/ZjUJ2pxnFyg@LQu@JNm0Kad9gkEKKk3qsBK2gzMKaNPQyM571ttX/58@KFk6NuBuWJEzpDRVTd7s45p3CGkqPAkhMywWNnERiy6xxabaRH4Wqzl6wUUcHc83cE/pjOvDZ1r2TvLRmL@MwbRctIfKuxYOonA7tI9E3xc/mQYV1A6bQPl2IKRjPqqYQL0PwzD/8fCx@7IHgcY6ubppVxAPLbMRZKSSxoqa8idV5xkfSn9eb@Ybum4gpOUslNd5Tur92FsMBCIPvfuxvHWyzLLw %}
+#! /usr/bin/env python3
+
+height = int((input().split())[0])
+width = height * 3
+half = int((height - 1) / 2)
+
+# Top half
+for line in range(1, half + 1):
+ non_dashes = ((line * 2) - 1)
+ dashes = int((width - (non_dashes * 3)) / 2)
+
+ print("%s%s%s" % ("-" * dashes, ".|." * non_dashes, "-" * dashes))
+
+# Middle line
+print("%s%s%s" % (
+ "-" * (int(width / 2) - 3),
+ "WELCOME",
+ "-" * (int(width / 2) - 3)
+))
+
+# Lower half
+for line in range(half, 0, -1):
+ non_dashes = ((line * 2) - 1)
+ dashes = int((width - (non_dashes * 3)) / 2)
+
+ print("%s%s%s" % ("-" * dashes, ".|." * non_dashes, "-" * dashes))
+{% endhighlight %}
+
+
+
+
+
+
+
+{% highlight perl6 tio=https://tio.run/##zZBBTsMwEEX3PsXgdpEgxVFSqSwiVqiLSi1skFiiIE@xpeBEdtpQhZyDo3CAHix0UpKGSuzxypp5/8/XL9Bm87adXEG4dTZ80SZEs4OCxoy97WGqUL@qEm5her28Fy7b2kJUuZVOKEylWJoy6bhKy1IR9iM4fMLstFFptgkGG68HAoh8OHxBnDA2gce8AALZJrcQCfFLVTM4PvIyuQlk6hS6zuqZzsQ@eSUDM9qfQgXH30hIyfzhNKlcugde84DDey9vai4@RDc4S5sLhiesofBrLWWGkGmDbOzl9QnoFqWc@c3TYnX3sF7U8DfCu0ZWeYX23Il3UYovLO7QOvzX7bTtDcTRNw %}
+#! /usr/bin/env perl6
+
+my $height = $*IN.slurp.words.head.Int;
+my $width = $height × 3;
+my $half-height = ($height - 1) ÷ 2;
+
+# Top half
+for 1..$half-height {
+ my $non-dashes = ($_ × 2) - 1;
+ my $dashes = ($width - ($non-dashes × 3)) ÷ 2;
+
+ say "{"-" x $dashes}{".|." x $non-dashes}{"-" x $dashes}";
+}
+
+# Middle line
+say "{"-" x (($width ÷ 2) - 3)}WELCOME{ "-" x (($width ÷ 2) - 3)}";
+
+# Lower half
+for (1..$half-height).reverse {
+ my $non-dashes = ($_ × 2) - 1;
+ my $dashes = ($width - ($non-dashes × 3)) ÷ 2;
+
+ say "{"-" x $dashes}{".|." x $non-dashes}{"-" x $dashes}";
+}
+{% endhighlight %}
+
+
+
+
+
+
+
+{% markdown %}
+### String Formatting
+
+In this challenge, you are to produce a table with four columns. The columns
+should contain the decimal, octal, hexadecimal and binary values of the row
+numbers. The function receives an int _number_. The table should contain that
+many rows, starting with row number 1.
+{% endmarkdown %}
+
+
+
+
+
+
+{% highlight python3 tio=https://tio.run/##jZFNCoMwEIX3nmIQhIQG0XYn9CwSTawpJoYYqVJ6dht/W7uQDlll3vveTKJ7W9bqMgyMF6CNUDYtaiOptZwh1cqMG5x44ErSLn0IZku4QsUV8p9Rkr38cJavWuxNYncJAoQCQ9WNo5jA3IcTxAtvLMZzIWnliI72YQkcmnvbWLRl4s1S53Y1JPV/lpJ3dJeUdDtjqzU36ACQCUVNv3iz49DNND0m8oMG1uNDAGgZhMyLkO/hyBKE8fD7EyNKKN1ahF33HL0B %}
+def print_formatted(number):
+ max_width = len("{0:b}".format(number))
+
+ for i in range(1, number + 1):
+ decimal = "{0}".format(i).rjust(max_width)
+ octal = "{0:o}".format(i).rjust(max_width)
+ hexadecimal = "{0:x}".format(i).upper().rjust(max_width)
+ binary = "{0:b}".format(i).rjust(max_width)
+
+ print("%s %s %s %s" % (decimal, octal, hexadecimal, binary))
+{% endhighlight %}
+
+
+
+
+
+
+
+{% highlight perl6 tio=https://tio.run/##XY9NCsIwEIX3PcUQIiRiB1tEBPEA3bhxK0iq0VZMWpIUK1KvXmNb/HurGWbeN29KaS7ztrVVCqXJtQuPhVHCOXkARnWlUmk43APwUjegStThNT@4DFYwjDEVVrKY48YZ3GfC2OV7vYeF1nn0yVsYGd0/jMYSqGuYcTwXuWYECIcHkK0my6BDeDtEiMOhIcZLv2Dskh8Z3U2A7vo8C/6po/lXE3Pe52uCpv17mdFxssaTdJhox9t4@gQ %}
+sub print-formatted ($number) {
+ my $max-width = $number.base(2).Str.chars;
+ my $format-string = ("%{$max-width}s" xx 4).join(" ") ~ "\n";
+
+ for 1..$number {
+ $format-string.printf($_, $_.base(8), $_.base(16), $_.base(2));
+ }
+}
+{% endhighlight %}
+
+
+
+
+
+
+
+{% markdown %}
+## Wrap-up
+
+This time I did not do all of the challenges, as the post would probably get
+too long. I still did 8 of them, and might do the rest of the string challenges
+in a later part anyway.
+
+I still find Perl 6 to produce much cleaner code, which is shown best with the
+first challenge. In Perl 6 (`$line.split(" ").join("-")`), I can read from left
+to right to see what I'm doing: I have a `$line`, which I split, and then join.
+In the Python variant (`"-".join(line.split(" "))`), it is much less clear what
+the actual item I'm working on is, as it's hidden inbetween the `join` and
+`split` calls.
+
+Of course, I'm still not an expert on Python 3 code, so I'm sure that there are
+many parts that could be written in a cleaner fashion. I'm still open for
+feedback to improve my Python 3 skills (hence I'm publishing these posts), so
+please let me know if you know better ways to solve some challenges.
+{% endmarkdown %}
diff --git a/content/posts/2018/_index.md b/content/posts/2018/_index.md
new file mode 100644
index 0000000..e1bb4e6
--- /dev/null
+++ b/content/posts/2018/_index.md
@@ -0,0 +1,3 @@
+---
+title: 2018
+---
diff --git a/content/posts/2021/2021-05-13-a-new-irc-client.md b/content/posts/2021/2021-05-13-a-new-irc-client.md
new file mode 100644
index 0000000..7003cb5
--- /dev/null
+++ b/content/posts/2021/2021-05-13-a-new-irc-client.md
@@ -0,0 +1,85 @@
+---
+date: 2021-05-13
+title: A New IRC::Client
+tags:
+- Raku
+- IRC
+- Programming
+social:
+ email: mailto:~tyil/public-inbox@lists.sr.ht&subject=A New IRC::Client
+---
+
+The Raku programming language has a popular module for creating IRC bots,
+[`IRC::Client`](https://github.com/raku-community-modules/IRC-Client). However,
+it's been stale for quite a while, and one of the bots I host,
+[Geth](https://github.com/Raku/geth), is having troubles on a regular basis.
+
+I've looked at the source code, and found a lot of neat tricks, but when
+maintaining a library, I generally want clean and straightforward code instead.
+To that end, I decided to just write my own from scratch. Given that [the IRC
+protocol is rather simple](https://tools.ietf.org/html/rfc2812), this was the
+easiest way to go about it.
+
+Sure enough, after a couple hours of playing around, I had something that
+worked reasonably well. A few more hours a day afterwards brought me to an
+`IRC::Client` that is usable in mostly the same way as the current
+`IRC::Client`, to save me effort in getting my current bots to make use of it
+for a few test runs.
+
+Geth was my main target, as I wanted it to stop from getting timed out so
+often. For the past week, Geth has been running stable, without any time
+out, so I think I've succeeded in the main goal.
+
+However, how to continue next? Since it is mostly compatible, but not
+*completely* compatible, if I were to adopt `IRC::Client` from the Raku
+ecosystem and push my version, many people's IRC bots would break when people
+update their dependencies. There is a solution for this built into the entire
+ecosystem, which is using the correct `:ver` and `:auth` (and in some cases,
+`:api`) so you can ensure your project is always getting the "correct"
+dependency. However, from personal experience, I know these additional
+dependency restrictions are rarely used in practice.
+
+I hope that with this blog post, I can inform the community in time about the
+changes that are coming to `IRC::Client`, so people have ample time to set
+their dependencies just right to keep their projects working. Of course, the
+real solution for the long term would be to make the small changes required to
+use the latest `IRC::Client` again.
+
+For convenience sake, I've added a small number of methods for backwards
+compatibility as well, though these will generate [a deprecation
+warning](https://docs.raku.org/routine/is%20DEPRECATED), and will be removed in
+a later `IRC::Client` release.
+
+There's two major changes that are not backwards compatible, however. The first
+one is the removal of the ability to have a single `IRC::Client` connect to
+multiple servers. This is also the easiest to remedy, by simply creating
+multiple instances of `IRC::Client`.
+
+The second major incompatible change is how events are dispatched to plugins.
+This used to be handled by going through all the plugins sequentially, allowing
+one plugin to stop the dispatch to another plugin. In my new version, events
+are dispatched to all plugins in parallel. This allows for faster execution,
+and for multiple plugins to handle an event without having to use `$*NEXT`
+(which has been removed). My main motivation is that a buggy plugin will no
+longer interfere with the interactions provided by other plugins. The ordering
+of your plugins will also stop being an issue to worry about.
+
+Geth's updates to actually use my updated `IRC::Client` module was introduced
+in
+[`edc6b08`](https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa),
+and most if it was updates to the way it handled logging. The actual changes
+needed to make Geth play nice were
+
+- [Adding the `IRC::Client::Plugin` role to `Geth::Plugin::Info`](https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa#diff-104b5cdb61f2aa423eb941ce32a4412b4cb814014728cae968e5aeff7dc587d2R14);
+- [And to `Geth::Plugin::Uptime`](https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa#diff-104b5cdb61f2aa423eb941ce32a4412b4cb814014728cae968e5aeff7dc587d2R24);
+- [Using `nicks` instead of `nick`](https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa#diff-104b5cdb61f2aa423eb941ce32a4412b4cb814014728cae968e5aeff7dc587d2R34);
+- [Using `start` instead of `run`](https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa#diff-104b5cdb61f2aa423eb941ce32a4412b4cb814014728cae968e5aeff7dc587d2R46);
+- [Using `privmsg` instead of `send`](https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa#diff-c388af832470886cdd4304aeb17c4c2f406ac184d33afb956b0ef8a92b69855bR57);
+
+The last two changes aren't strictly necessary, as there are backwards
+compatibility methods made for these, but it's a rather small change and
+reduces the amount of noise in the logs.
+
+With this, I hope everyone using `IRC::Client` is prepared for the coming
+changes. If you have any comments or questions, do not hesitate to reach out to
+me and share your thoughts!
diff --git a/content/posts/2021/2021-05-22-raku-on-libera-chat.md b/content/posts/2021/2021-05-22-raku-on-libera-chat.md
new file mode 100644
index 0000000..87ce7ff
--- /dev/null
+++ b/content/posts/2021/2021-05-22-raku-on-libera-chat.md
@@ -0,0 +1,35 @@
+---
+date: 2021-05-22
+title: Raku is moving to Libera.chat
+tags:
+- Raku
+- LiberaChat
+- IRC
+social:
+ email: mailto:~tyil/public-inbox@lists.sr.ht&subject=Raku is moving to Libera.chat
+---
+
+Earlier this week, the staff at the Freenode IRC network have resigned en
+masse, and created a new network, Libera. This was sparked by [new ownership of
+Freenode](https://kline.sh/). Due to concerns with the new ownership, the Raku
+Steering Council has decided to also migrate our IRC channels to Libera.
+
+This requires us to take a couple steps. First and foremost, we need to
+register the Raku project, to allow us a claim to the `#raku` and related
+channels. Approval for this happened within 24 hours, and as such, we can
+continue on the more noticable steps.
+
+The IRC bots we're using for various tasks will be moved next, and the Raku
+documentation has to be updated to refer to Libera instead of Freenode. The
+coming week we'll be working on that, together with the people who provide
+those bots.
+
+Once this is done, the last step involves the Matrix bridge. Libera and
+Matrix.org staff are working on this, but there's no definite timeline
+available just yet. This may mean that Matrix users will temporarily not be
+able to join the discussions happening at Libera. We will keep an eye on the
+progress of this, and set up the bridge as soon as it has been made available.
+
+If you have any questions regarding the migration, feel free to reach out to us
+via email (`rsc@raku.org`) or on IRC (`#raku-steering-council` on
+irc.libera.chat).
diff --git a/content/posts/2021/2021-06-04-managing-docker-compose-projects-with-openrc.md b/content/posts/2021/2021-06-04-managing-docker-compose-projects-with-openrc.md
new file mode 100644
index 0000000..c182654
--- /dev/null
+++ b/content/posts/2021/2021-06-04-managing-docker-compose-projects-with-openrc.md
@@ -0,0 +1,194 @@
+---
+date: 2021-06-04
+title: Managing Docker Compose with OpenRC
+tags:
+- Gentoo
+- OpenRC
+- Docker
+- DockerCompose
+---
+
+On one of my machines, I host a couple services using `docker-compose`. I
+wanted to start/restart/stop these using the default init/service manager used
+on the machine, `openrc`. This would allow them to start/stop automatically
+with Docker (which coincides with the machine powering on or off,
+respectively).
+
+I've set this up through a single `docker-compose` meta-service. To add new
+`docker-compose` projects to be managed, all I need to do for `openrc`
+configuration is creating a symlink, and configure the path to the
+`docker-compose.yaml` file.
+
+The meta-service lives at `/etc/init.d/docker-compose`, just like all other
+services managed by `openrc`. This file is quite straightforward. To start off,
+a number of variables are set and exported.
+
+```sh
+name="$RC_SVCNAME"
+description="OpenRC script for managing the $name docker-compose project"
+
+# Set default values
+DOCKER_COMPOSE="${DOCKER_COMPOSE:-docker-compose} $DOCKER_COMPOSE_ARGS"
+
+COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-$name}"
+
+# Export all variables used by docker-compose CLI
+export COMPOSE_PROJECT_NAME
+export COMPOSE_FILE
+export COMPOSE_PROFILES
+export COMPOSE_API_VERSION
+export DOCKER_HOST
+export DOCKER_TLS_VERIFY
+export DOCKER_CERT_PATH
+export COMPOSE_HTTP_TIMEOUT
+export COMPOSE_TLS_VERSION
+export COMPOSE_CONVERT_WINDOWS_PATHS
+export COMPOSE_PATH_SEPARATOR
+export COMPOSE_FORCE_WINDOWS_HOST
+export COMPOSE_IGNORE_ORPHANS
+export COMPOSE_PARALLEL_LIMIT
+export COMPOSE_INTERACTIVE_NO_CLI
+export COMPOSE_DOCKER_CLI_BUILD
+```
+
+One of the services I use is also configured with its own `external` network. I
+want it to be created if it doesn't exist, to ensure that the service can start
+up properly. I do *not* want it to be removed, so I left that out.
+
+```sh
+# Set up (external) networks
+for name in "${DOCKER_NETWORKS[@]}"
+do
+ # Create the network if needed
+ if ! docker network ls | awk '{ print $2 }' | grep -q "$name"
+ then
+ einfo "Creating docker network '$name'"
+ docker network create "$name" > /dev/null
+ fi
+
+ # Expose some variables for the networks
+ network_id="DOCKER_NETWORK_${name}_ID"
+
+ declare -gx DOCKER_NETWORK_${name}_ID="$(docker network ls | awk '$2 == "'"$name"'" { print $1 }')"
+ declare -gx DOCKER_NETWORK_${name}_GATEWAY="$(docker network inspect "${!network_id}" | jq -r '.[0].IPAM.Config[0].Gateway')"
+
+ unset network_id
+done
+```
+
+And lastly, there's the four simple functions to declare dependencies,
+configure how to `start` or `stop`, and how to get the `status` of the service.
+
+```sh
+depend() {
+ need docker
+}
+
+start() {
+ $DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" up -d
+}
+
+status() {
+ $DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" ps
+}
+
+stop() {
+ $DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" down
+}
+```
+
+Now, to actually create a service file to manage a `docker-compose` project, a
+symlink must be made. I'll take my
+[`botamusique`](https://github.com/azlux/botamusique) service as an example.
+
+```
+ln -s /etc/init.d/docker-compose /etc/init.d/botamusique
+```
+
+This service can't start just yet, as there's no `$COMPOSE_PROJECT_DIRECTORY`
+configured for it yet. For this, a similarly named file should be made in
+`/etc/conf.d`. In here, any variable used by the service can be configured.
+
+```
+$EDITOR /etc/conf.d/botamusique
+```
+
+In my case, it only pertains the `$COMPOSE_PROJECT_DIRECTORY` variable.
+
+```
+COMPOSE_PROJECT_DIRECTORY="/var/docker-compose/botamusique"
+```
+
+And that's it. For additional `docker-compose` projects I need to make only a
+symlink and a configuration file. If I discover a bug or nuisance, only a
+single file needs to be altered to get the benefit on all the `docker-compose`
+services.
+
+For reference, here's the full `/etc/init.d/docker-compose` file.
+
+```sh
+#!/sbin/openrc-run
+# Copyright 2021 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+name="$RC_SVCNAME"
+description="OpenRC script for managing the $name docker-compose project"
+
+# Set default values
+DOCKER_COMPOSE="${DOCKER_COMPOSE:-docker-compose} $DOCKER_COMPOSE_ARGS"
+
+COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-$name}"
+
+# Export all variables used by docker-compose CLI
+export COMPOSE_PROJECT_NAME
+export COMPOSE_FILE
+export COMPOSE_PROFILES
+export COMPOSE_API_VERSION
+export DOCKER_HOST
+export DOCKER_TLS_VERIFY
+export DOCKER_CERT_PATH
+export COMPOSE_HTTP_TIMEOUT
+export COMPOSE_TLS_VERSION
+export COMPOSE_CONVERT_WINDOWS_PATHS
+export COMPOSE_PATH_SEPARATOR
+export COMPOSE_FORCE_WINDOWS_HOST
+export COMPOSE_IGNORE_ORPHANS
+export COMPOSE_PARALLEL_LIMIT
+export COMPOSE_INTERACTIVE_NO_CLI
+export COMPOSE_DOCKER_CLI_BUILD
+
+# Set up (external) networks
+for name in "${DOCKER_NETWORKS[@]}"
+do
+ # Create the network if needed
+ if ! docker network ls | awk '{ print $2 }' | grep -q "$name"
+ then
+ einfo "Creating docker network '$name'"
+ docker network create "$name" > /dev/null
+ fi
+
+ # Expose some variables for the networks
+ network_id="DOCKER_NETWORK_${name}_ID"
+
+ declare -gx DOCKER_NETWORK_${name}_ID="$(docker network ls | awk '$2 == "'"$name"'" { print $1 }')"
+ declare -gx DOCKER_NETWORK_${name}_GATEWAY="$(docker network inspect "${!network_id}" | jq -r '.[0].IPAM.Config[0].Gateway')"
+
+ unset network_id
+done
+
+depend() {
+ need docker
+}
+
+start() {
+ $DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" up -d
+}
+
+status() {
+ $DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" ps
+}
+
+stop() {
+ $DOCKER_COMPOSE --project-directory "$COMPOSE_PROJECT_DIRECTORY" down
+}
+```
diff --git a/content/posts/2021/_index.md b/content/posts/2021/_index.md
new file mode 100644
index 0000000..1287d56
--- /dev/null
+++ b/content/posts/2021/_index.md
@@ -0,0 +1,3 @@
+---
+title: 2021
+---
diff --git a/content/posts/_index.md b/content/posts/_index.md
new file mode 100644
index 0000000..b80ef2e
--- /dev/null
+++ b/content/posts/_index.md
@@ -0,0 +1,24 @@
+---
+title: Blog
+---
+
+Over time, I've written a number of articles. Some to voice my opinion, some to
+help people out with a tutorial. These articles are listed below, sorted by
+publication date. If you have any comments on any of my articles, feel free to
+reach out to me through any of the contact details found [on the
+homepage][home]. Alternatively, I have a public inbox on Sourcehut that you
+can send any comments to:
+[`~tyil/public-inbox@lists.sr.ht`](mailto:~tyil/public-inbox@lists.sr.ht).
+
+All my blog posts are available under the [Creative Commons (CC BY-SA
+3.0)][cc-by-sa] license, which means you are free to use it for any purpose so
+long as you keep attribution to me (and preferably also just link to the
+original article) and do not relicense the article.
+
+I'd also like to note that **these articles reflect my opinion, and only
+mine**. Please refrain from accusing other people of holding my opinion for
+simply being referenced in my articles.
+
+[cc-by-sa]: https://creativecommons.org/licenses/by-sa/3.0/
+[home]: /
+[pgp]: http://pgp.mit.edu/pks/lookup?op=vindex&search=0x7A6AC285E2D98827
--
cgit v1.1