[^]*<\/head>"
-
-# Define glob expressions to ignore
-ignores:
- - "*.autogen.md"
-
-# Use a plugin to recognize math
-#markdownItPlugins:
-# -
-# - "@iktakahiro/markdown-it-katex"
-
-# Disable inline config comments
-noInlineConfig: true
-
-# Disable progress on stdout
-noProgress: true
-
-# Use a specific formatter
-outputFormatters:
- -
- - markdownlint-cli2-formatter-default
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index a88615e..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,8 +0,0 @@
-FROM fleek/hugo:node-16
-
-LABEL MAINTAINER="Davide Polonio poloniodavide@gmail.com"
-LABEL DESCRIPTION="Docker image for deploying website on IPFS via fleek"
-
-RUN apt-get update \
- && apt-get install -y git-lfs \
- && rm -rf /var/lib/apt/lists/*
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2b8abdd
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+.PHONY: all
+.DEFAULT_GOAL := all
+
+all: clean html
+
+html:
+ @./publish.el
+
+watch:
+ @fswatch -0 path | while read -d "" event \
+ do;
+ ./publish.el "${event}";
+ done
+
+clean:
+ @rm -rf ./dist/
diff --git a/archetypes/default.md b/archetypes/default.md
deleted file mode 100644
index 00e77bd..0000000
--- a/archetypes/default.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: "{{ replace .Name "-" " " | title }}"
-date: {{ .Date }}
-draft: true
----
-
diff --git a/config.toml b/config.toml
deleted file mode 100644
index 64ed254..0000000
--- a/config.toml
+++ /dev/null
@@ -1,73 +0,0 @@
-baseURL = "https://bitdispenser.dev/"
-languageCode = "en-us"
-defaultContentLanguage = "en"
-title = "Bitdispenser"
-theme = "hermit"
-#enableGitInfo = true
-pygmentsCodefences = true
-pygmentsUseClasses = true
-rssLimit = 15
-copyright = "This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License."
-enableEmoji = true
-relativeUrls = true
-
-[author]
- name = "Davide Polonio"
-
-[taxonomies]
- tag = "tags"
-
-[params]
- dateform = "2 Jan 2006"
- dateformShort = "2 Jan"
- dateformNum = "02-01-2006"
- dateformNumTime = "02-01-2006"
- themeColor = "#494f5c"
- homeSubtitle = "Yet another ordered dispenser of bits"
- footerCopyright = ' · CC BY-SA 4.0'
- justifyContent = true
- relatedPosts = true
- code_copy_button = true
-
- [[params.social]]
- name = "email"
- url = "mailto:davide+bitdispenser@poldebra.me"
- [[params.social]]
- name = "github"
- url = "https://github.com/Polpetta"
- [[params.social]]
- name = "stackoverflow"
- url = "https://stackoverflow.com/users/9306378/polpetta"
- [[params.social]]
- name = "linkedin"
- url = "https://www.linkedin.com/in/davidepolonio"
-
-[languages]
- [languages.en]
- languageName = "English"
- contentDir = "content/english"
- title = "Bitdispenser"
- homeSubtitle = "Yet another ordered dispenser of bits"
- weight = 0
- [[languages.en.menu.main]]
- name = "Posts"
- url = "posts/"
- weight = 10
- [[languages.en.menu.main]]
- name = "About me"
- url = "about-me"
- weight = 20
- [languages.it]
- languageName = "Italiano"
- contentDir = "content/italian"
- title = "Bitdispenser"
- homeSubtitle = "L'ennesimo distributore ordinato di bit"
- weight = 10
- [[languages.it.menu.main]]
- name = "Articoli"
- url = "articoli/"
- weight = 10
- [[languages.it.menu.main]]
- name = "Riguardo l'Autore"
- url = "riguardo-l-autore"
- weight = 20
\ No newline at end of file
diff --git a/content/english/about-me.md b/content/english/about-me.md
deleted file mode 100644
index 4b1a9c9..0000000
--- a/content/english/about-me.md
+++ /dev/null
@@ -1,56 +0,0 @@
----
-title: "About me"
----
-
-Hi all! I am Davide Polonio, a programmer and devOps engineer. I am born in 1994
-and started messing with computers since I was 8 (my first OS was the only and
-glorious Windows 95), and from there I continued my path to learn about
-computers and how to ~~break~~ tweak them. I graduated at university of Padova
-with a master degree in computer science in 2018.
-
-I prefer back-end stuff (I usually end up using Java, Golang, Rust and Bash to
-gluing stuff around), especially automating releases with Continuous Integration
-and Continuous Delivery. I create and manage pipelines for Jenkins (and I
-currently administer Jenkins as well), but I also know other CI systems like
-Github Actions, Travis CI, Drone CI (or the now clone Woodpecker). I enjoy
-container technologies, starting from Docker, Podman and going on to
-orchestrators like Docker Swarm, Kubernetes and Nomad (which I would like to
-learn more of the lasts two). If I can, I use Infrastructure as Code (IaC) tools
-like Terraform and Packer to create and maintain infrastructure on cloud
-providers, like AWS or Scaleway (I also maintain [Podman plugin for
-Packer](https://github.com/Polpetta/packer-plugin-podman)). Automation is
-usually my preferred way, since it helps developers focus only on the business
-product instead of messing with tasks that can be done by a machine. Finally, I
-am interested in development processes like (but not only) Agile, Scrum,
-Waterfall. I think the right way to develop a product starts from how you
-organize your workflow and what processes you put in place in order to keep the
-_development machine_ going.
-
-I am an open source enthusiast, so when I am able in my free time (or paid time
-if it is an open source business project) I like to contribute to open source
-projects and discover new ones. I believe in the added values that open source
-software (and especially free software) can give respect to closed ones. My
-dream job would be to contribute to one of the many open source I like full
-time! 🤩
-
-I'm not only into programming though! I like to practice sport. In particular, I
-am a black belt Judo and I teach it at the local dojo were I live. I know how to
-ski from when I was I child, and if possible I enjoy passing some time in the
-middle of mountains to admire the beauty of nature in winter. Finally, I like to
-play tabletops games and I enjoy things like table football, billiards, bowling
-(although I suck at the last one 😅).
-
-If you are interested in getting in touch with me (or to get my resume) you can
-send me an e-mail (I prefer this way) at
-[davide@poldebra.me](mailto:davide+bitdispenser@poldebra.me) or you can contact
-me through my [LinkedIn](https://www.linkedin.com/in/davidepolonio/) profile.
-
-## Why this website and this blog
-
-As I have already written at the beginning of [my first blog post]({{< ref
-"/hello-world.md" >}}) I liked the idea of a place where I can share my thoughts
-regarding technologies, personal experiences and what not. This blog is open
-source, and all my content is licensed under CC BY-SA 4.0. The repository can be
-found either on [my personal Gitea
-instance](https://git.poldebra.me/polpetta/bitdispenser.dev) or on [Github
-(mirrored)](https://github.com/Polpetta/bitdispenser.dev).
diff --git a/content/english/posts/hello-world.md b/content/english/posts/hello-world.md
deleted file mode 100644
index 41cf0f0..0000000
--- a/content/english/posts/hello-world.md
+++ /dev/null
@@ -1,150 +0,0 @@
----
-title: Hello world!
-description: The very first article of this blog
-tags:
-- blog
-- tech
-- hugo
-date: 2021-05-18T22:22:00+02:00
----
-
-If you are reading this it means either you are trying to understand who I am
-(are you a recruiter by any chance?) or you are simply bored. In any way, this
-is the very first blog post, and it means you have come to the end of the road
-(since this is the beginning). I always loved to have a place where I can write
-down my thoughts about a new technology, a framework or what I did in order to
-achieve a goal in a hobby project. That is not all though: I was searching for a
-place where I was also able to express my ideas regarding modern dilemmas like
-privacy issues and decentralization. I am not the kind of guy who likes social
-networks, so I never had the chance to express them. Until now.
-
-## How this blog is built
-
-### The website
-
-This blog is a very simple [Hugo](https://gohugo.io/) website. If you do not
-know what it is, its basically a static website generator. It takes Markdown
-documents and converts them in HTML pages. If you applied a specific theme then
-it builds the page according to that theme. Hugo has a huge selection of themes
-in its [dedicated themes page](https://themes.gohugo.io/), so basically picking
-up one and starting from there is very simple. If you are curious about how I
-made it, you can check out the source at my [personal git server
-instance](https://git.poldebra.me/polpetta/bitdispenser.dev), where I started
-hosting my code when [Microsoft bought
-Github](https://news.microsoft.com/announcement/microsoft-acquires-github/) (you
-can find a copy of the repository on Github too, visiting [my
-profile](https://github.com/Polpetta)). I have simply picked up the simpler and
-cleanest blog theme out there, following Ludwig Mies van der Rohe's idea:
-
-> less is more
-
-It will be a success if more than two people actually starts reading what I
-write here, at least I want them to read this blog without having their
-eyes bleeding with an extravagant color combination.
-
-Last but not least, the website icon is literally the Team Fortress 2 dispenser
-(one of the games I love and one of the first game I started playing online as a
-kid when a decent connection at home was finally available), taken from the RED
-team and generated via a [favicon generator](https://realfavicongenerator.net/).
-The name of the website partially came out from that, and from the fact that
-basically every website is a dispenser of bits, and it is only thanks to our
-beloved browsers we are able to actually "consume" what is distributed in the
-first place.
-
-{{< figure src="/content/hello-world/engiwithdispenser.png" alt=`Engineer with
-his dispenser` caption=`Engineer class with a dispenser. Thanks to [Team
-Fortress wiki](https://wiki.teamfortress.com) for providing the image I
-shamelessly downloaded from them` >}}
-
-I hope Valve will not sue me for taking that asset as my website favicon. I
-swear I will change it, a day. Pinky promise.
-
-### Hosting
-
-The real problem of hosting a website now day is not how to build it (as you can
-see) but _where_ you can host it. There are plenty of cloud provides: AWS,
-Google Cloud, DigitalOcean, Scaleway, etc... every day there is a new one
-popping up. They all offers the possibility to host your website pretty easily,
-especially if the website is a static one like this (e.g. using a S3 bucket).
-Since I like challenges and I also like to learn new stuff, I though that
-hosting the website in this way was boring. At the same time, I wanted to have a
-good uptime and to not meddle too much under the hood. My (dream) requirements
-were:
-
-* always up
-* good response time
-* possibility to host as much data as I want
-* using a hosting free as in beer and (possibly) that could use free as in
- freedom technologies
-
-Now, reading this I imagine you are thinking I am going crazy, and maybe I am, but
-that is not the case. In fact, multiple weeks prior to writing this article,
-while trying to kill the boredom caused by COVID-19 lockdown, I discovered
-[IPFS](https://ipfs.io/). I already heard of it at University, but I never
-bothered too much to understand what was about. I though "well, it surely is
-some sort of filesystem". I was somewhat right, but not the way I thought.
-
-IPFS acts like a peer-to-peer network, where nodes hash the content they want to
-share to let other nodes grab it. You can grab this content using your local
-node or using one of the many available gateways. Nodes can "pin" a file too, in
-order to keep it locally and to serve it to other nodes. If a file gets pinned
-by different nodes and gains traction it basically can not be deleted from the
-web.
-
-#### Choosing the right tools
-
-Surely, as [the IPFS documentation
-describes](https://docs.ipfs.io/how-to/websites-on-ipfs/multipage-website/), I
-could have done it by myself. But there is a but. Holding all the infrastructure
-manually means sacrifice the "always up" and "good response time" thingy for two
-simple motives:
-
-1. the [server where I host my drafts & codebase](https://git.poldebra.me) is
- hosted on a very small machine (2GB of RAM and 2vCPU), very easy to kill with
- the slightest of loads
-2. in order to achieve a good response time, a CDN or some sort of caching is
- necessary (even if the application is stored in a distributed file system)
-
-{{< figure src="/content/hello-world/fleek.png" caption=`Fleek website, that I
-used to automate my deployment on IPFS and my DNS update` alt=`The fleek website
-screenshot` class="right" >}}
-
-Finally, in order to achieve full automation with DNS updates, I would have
-needed to implement and use NameCheap APIs (currently it is the DNS provider I
-use for most of my websites). "What's the difficulty?" one would ask. [Here is
-the official documentation](https://www.namecheap.com/support/api/intro/), and
-even if the APIs look promising, while studying them my will to live decreased a
-little bit, and so I decided that if I wanted to get up and running with less
-maintenance as possible, with a good uptime while having the maximum
-automation possible I needed to rely on a dedicated service. Luckily
-[fleek.co](https://fleek.co) was what I was searching for. They currently
-provide the possibility to buy a domain from their website, giving them all the
-hassle of updating the new website on IPFS, distributing it, refreshing a very
-possible CDN and finally to update the DNS records accordingly. This, as you can
-imagine, provides multiple benefits:
-
-* I do not have to care about my very little dev machine getting hugged to death
- by request in the remote case any of my posts get any attention
-* I do not have to focus on automating the process someone else has already done
- for me
-* I can focus on writing posts after dinner instead of scratching my head trying
- to understand why the website does not load/the DNS is not properly updated
-
-The only downside to this approach is that Fleek provides limitations on how
-much data and bandwidth you can host. At the time of writing, you can only host
-up to 3GB (enough for this website) and have a 50GB bandwidth (that is fine for
-now) for the free version. [Upgrading you account](https://fleek.co/pricing/) to
-one of the available plans give you extra space and bandwidth.
-
-## Future improvements
-
-For sure, this is only the beginning. Having an automatic workflow of spell
-check, deployment and release would be the first milestone. Future features for
-this website could be an automatic posting of every new article on a dedicated
-Mastodon bot, so that people can possibly discuss about my thoughts on the
-fediverse, a decentralized network.
-
-To conclude, the frequency of this blog will be...whenever I have time to post
-:grin: Of course I need content before posting something, and this require some
-time for myself for experimenting with new technologies and learning new stuff,
-so I do not expect very much posting, but only time will tell!
diff --git a/content/english/posts/songlify-1.md b/content/english/posts/songlify-1.md
deleted file mode 100644
index 9840ffb..0000000
--- a/content/english/posts/songlify-1.md
+++ /dev/null
@@ -1,255 +0,0 @@
----
-title: "Building a Telegram bot in Rust: a journey through Songlify"
-description: An adventure through Rust and Telegram
-tags:
-- blog
-- tech
-- rust
-- telegram
-- songlify
-date: 2022-01-05T17:01:00+02:00
----
-
-Some time passed since the last article I wrote there. A lot of stuff happened
-meanwhile, especially with COVID, but here we are again. While busy dealing with
-the mess of real life tasks, three months ago I started to write a little bot
-for Telegram in Rust. It is a simple one, but I consider the journey interesting
-and worth of writing it down. If I add new features worth mentioning I will
-start a series about it, maybe.
-
-## Telegram bots
-
-Telegram bots are not something new to me and nowadays are pretty much easy to
-make, so I consider them like a gym where to try out new technologies and
-experiment with stuff. I wrote plentiful of them, some of those are open source
-like for example (which is
-currently broken 😭) that was useful when going to University, because it
-scraped the university free room web page and from there it was able to tell you
-which rooms where without any lessons and for how much time, allowing you to
-easily find a place where to study with your mates (yep, we didn't like library
-too much).
-
-{{< figure src="/content/songlify/telegramscreen.png" alt=`A screenshot of
-TorreArchimedeBot in action.` caption=`A screenshot of TorreArchimedeBot in
-action.` >}}
-
-Also another one bot worthy of mention is
-, that allowed our D&D group to
-receive push notifications of our private Subreddit in our Telegram group.
-
-As you can see, all of these bots are quite simple, but they have the added
-value of teaching you some new programming concepts, technologies or frameworks
-that can be later applied in something that can be more production environment.
-
-## Rust
-
-I started to approach Rust many years ago (I do not remember exactly when).
-First interaction with it was quite interesting to say at least: there were way
-less compiler features (for example now the compiler is able to understand
-object lifetime at compile time most of the time alone, without specifying them)
-that made it a... _not-so-pleasant programming experience_. It had potential
-thought, so by following Rust news I picked it up last year again, noticing that
-now it has improved a lot and it is more pleasant to write. Meanwhile, also
-JetBrains developed a good support for IntelliJ, so now it is even possible to
-debug and perform every operation directly from your IDE UI.
-
-## Making the two worlds collide: Rust + Telegram
-
-One of the features I wanted to learn this time regarding Rust was the
-asynchronous support it offers. Rust started to have `async` support with
-[Tokio](https://tokio.rs/) framework, and recently the Rust team started to
-build the asynchronous functionality inside Rust itself. Even if in the first
-steps, it looks promising and the idea of a low-level language, without GC, with
-automatic memory management and so much safety having asynchronous support is
-exciting to me! 🥳 So the only option left, at this point, was to start messing
-around with it. I started by picking up one of the many frameworks that provides
-a layer for the Telegram APIs, [Teloxide](https://github.com/teloxide/teloxide).
-In particular, as you can see from its _README_, one of the examples starts by
-using `#[tokio:main]` macro:
-
-```rust
-use teloxide::prelude::*;
-
-#[tokio::main]
-async fn main() {
- teloxide::enable_logging!();
- log::info!("Starting dices_bot...");
-
- let bot = Bot::from_env().auto_send();
-
- teloxide::repl(bot, |message| async move {
- message.answer_dice().await?;
- respond(())
- })
- .await;
-}
-```
-
-This was the reason I picked it up, given that it looked the most promising by
-the time I started the project.
-
-### Building _Songlify_
-
-So, after choosing what was going to use to build the bot, I needed a _reason_
-to build it.
-
-With my friends we usually share a lot of songs (via Spotify links), so I
-thought it was a good idea to build a bot around it. I integrated a Spotify API
-library in it and started hacking up a bot.
-
-> ⚠ Note that at the time of writing I have just noticed that the library I use
-> for speaking with Spotify, [aspotify](https://crates.io/crates/aspotify) has
-> been deprecated in favour of [rspotify](https://crates.io/crates/rspotify)
-
-The first bot version was something very simple, and it was a single-file
-program with nothing very fancy (I have written it in a night):
-
-```rust
-use crate::SpotifyURL::Track;
-use aspotify::{Client, ClientCredentials};
-use teloxide::prelude::*;
-
-enum SpotifyURL {
- Track(String),
-}
-
-fn get_spotify_entry(url: &str) -> Option {
- if url.contains("https://open.spotify.com/track/") {
- let track_id = url.rsplit('/').next().and_then(|x| x.split('?').next());
- return match track_id {
- Some(id) => Some(SpotifyURL::Track(id.to_string())),
- None => None,
- };
- }
- return None;
-}
-
-struct TrackInfo {
- name: String,
- artist: Vec,
-}
-
-async fn get_spotify_track(spotify: Box, id: &String) -> Option {
- match spotify.tracks().get_track(id.as_str(), None).await {
- Ok(track) => Some(TrackInfo {
- name: track.data.name,
- artist: track.data.artists.iter().map(|x| x.name.clone()).collect(),
- }),
- Err(_e) => None,
- }
-}
-
-#[tokio::main]
-async fn main() {
- teloxide::enable_logging!();
- log::info!("Starting Songlify...");
-
- let bot = Bot::from_env().auto_send();
- teloxide::repl(bot, |message| async move {
- let spotify_creds =
- ClientCredentials::from_env().expect("CLIENT_ID and CLIENT_SECRET not found.");
- let spotify_client = Box::new(Client::new(spotify_creds));
-
- log::info!("Connected to Spotify");
- let text = message.update.text().and_then(get_spotify_entry);
- match text {
- Some(spotify) => match spotify {
- Track(id) => {
- let track_info = get_spotify_track(spotify_client, &id).await;
- match track_info {
- Some(info) => {
- let reply = format!(
- "Track information:\n\
- Track name: {}\n\
- Artists: {}",
- info.name,
- info.artist.join(", ")
- );
- Some(message.reply_to(reply).await?)
- }
- None => None,
- }
- }
- },
- None => None,
- };
- respond(())
- })
- .await;
-
- log::info!("Exiting...");
-}
-```
-
-As you can see, basically every time a request arrived to the bot, login to
-Spotify was performed and track information and name retrieved from there. Of
-course this was only the beginning (also you can "appreciate" the number of
-nested blocks there...). Now the bot supports albums and playlists too, with the
-possibility to go through each song in the playlist and collect general
-information such as how many artists are in that playlist, how many songs and
-other little information like that. If you see the [bot
-repository](https://git.poldebra.me/polpetta/Songlify) you can see now that
-Spotify functions live in a separate module.
-
-#### Packaging and distribution
-
-The obvious choice for a software like that was to incorporate it into a OCI
-image. I wrote a very simple Dockerfile that, once the program was built, took
-the artifact and using the [multi-stage Docker build
-functionality](https://docs.docker.com/develop/develop-images/multistage-build/)
-and put it into a separate container, in order to avoid having build
-dependencies inside the final image. I used the images distributed by the
-[Distroless project](gcr.io/distroless/) (you can find the source on their
-[Github repository](https://github.com/GoogleContainerTools/distroless)) in
-order to obtain the smallest possible image. The final result?
-
-```txt
-λ ~/Desktop/git/songlify/ docker images
-REPOSITORY TAG IMAGE ID CREATED SIZE
-test/test latest 8ac7a7018719 5 seconds ago 34MB
- 4bc7fb0699e0 12 seconds ago 1.53GB
-rust 1.56.1-slim-bullseye d3e070c5ffa7 6 weeks ago 667MB
-gcr.io/distroless/base latest-amd64 24787c1cd2e4 52 years ago 20.2MB
-```
-
-A part from the 52 years old image pulled from `gcr`, you can see that
-`test/test` (actually Songlify) is only of **34MB**. Not much if you consider
-that inside that image there are shared dynamic libraries to make the executable
-able to run, which by default weights 20MB. A plus of these images is that they
-do not run as root user and they do not have any shell of bash integrated,
-making a possible surface attack smaller (not that Docker is secure anyway...).
-I upload the images on Docker Hub, where you can find them here
-
-
-Finally, to run the bot I use a very simple docker-compose definition, that can
-be found in my [server-dotfiles
-repository](https://git.poldebra.me/polpetta/server-dotfiles/src/commit/7e7e1780b2db45f475510c49bf1a2f9e76c4c166/songlify/docker-compose.yml).
-This allows me to easily upgrade the bot by just changing the version and
-running `docker-compose up -d`.
-
-#### Plans for the future
-
-Currently I work on the bot only when I feel like I wanna add something. An
-interesting feature to add could be to insert a persistence layer (using a
-database for instance) and add various stats (which is the most shared song? Who
-_is that guy_ that shares the most songs in a group?). Persistence can be
-achieved quite easily by using [Diesel](https://diesel.rs/), an ORM compatible
-with various databases.
-
-Another cool feature could be to add the _inline bot_ functionality, where you
-can search for songs directly in Spotify. Since I currently have a domain
-available for that I could set it up for receive web-hook notifications, instead
-of performing polling like the bot is currently doing (one requisite for inline
-bots is indeed to receive web-hooks). There are platforms like Heroku where you
-could make the bot run, but currently I prefer to use my box since it gives me
-more flexibility. Experimenting with Heroku could lead to cool results though
-😏.
-
-Finally, link translation could be something very useful. I have a friend that
-does not use Spotify but prefers to listen to music via YouTube. So an
-interesting feature would be, given a Spotify link, to "convert" it into a
-YouTube link and, of course, _vice-versa_. This could lead to translate
-playlists and albums too into YouTube-based playlists, which of course could be
-very useful if you are trying to avoid the infamous _vendor lock-in_, in this
-case being stuck with Spotify because all you music collection, saved songs, etc
-is there.
diff --git a/content/italian/riguardo-l-autore.md b/content/italian/riguardo-l-autore.md
deleted file mode 100644
index a3ac27c..0000000
--- a/content/italian/riguardo-l-autore.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-Title: "Riguardo l'Autore"
----
-
-Prova 😀
diff --git a/layouts/partials/header.html b/layouts/partials/header.html
deleted file mode 100644
index 7bfd670..0000000
--- a/layouts/partials/header.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/org/about-me.org b/org/about-me.org
new file mode 100644
index 0000000..9141c8e
--- /dev/null
+++ b/org/about-me.org
@@ -0,0 +1,60 @@
+Hi all! I am Davide Polonio, a programmer and devOps engineer. I am born
+in 1994 and started messing with computers since I was 8 (my first OS
+was the only and glorious Windows 95), and from there I continued my
+path to learn about computers and how to +break+ tweak them. I graduated
+at university of Padova with a master degree in computer science
+in 2018.
+
+I prefer back-end stuff (I usually end up using Java, Golang, Rust and
+Bash to gluing stuff around), especially automating releases with
+Continuous Integration and Continuous Delivery. I create and manage
+pipelines for Jenkins (and I currently administer Jenkins as well), but
+I also know other CI systems like Github Actions, Travis CI, Drone CI
+(or the now clone Woodpecker). I enjoy container technologies, starting
+from Docker, Podman and going on to orchestrators like Docker Swarm,
+Kubernetes and Nomad (which I would like to learn more of the lasts
+two). If I can, I use Infrastructure as Code (IaC) tools like Terraform
+and Packer to create and maintain infrastructure on cloud providers,
+like AWS or Scaleway (I also maintain
+[[https://github.com/Polpetta/packer-plugin-podman][Podman plugin for Packer]]).
+Automation is usually my preferred way, since it helps developers focus
+only on the business product instead of messing with tasks that can be
+done by a machine. Finally, I am interested in development processes
+like (but not only) Agile, Scrum, Waterfall. I think the right way to
+develop a product starts from how you organize your workflow and what
+processes you put in place in order to keep the /development machine/
+going.
+
+I am an open source enthusiast, so when I am able in my free time (or
+paid time if it is an open source business project) I like to contribute
+to open source projects and discover new ones. I believe in the added
+values that open source software (and especially free software) can give
+respect to closed ones. My dream job would be to contribute to one of
+the many open source I like full time! 🤩
+
+I'm not only into programming though! I like to practice sport. In
+particular, I am a black belt Judo and I teach it at the local dojo were
+I live. I know how to ski from when I was I child, and if possible I
+enjoy passing some time in the middle of mountains to admire the beauty
+of nature in winter. Finally, I like to play tabletops games and I enjoy
+things like table football, billiards, bowling (although I suck at the
+last one 😅).
+
+If you are interested in getting in touch with me (or to get my resume)
+you can send me an e-mail (I prefer this way) at
+[[mailto:davide+bitdispenser@poldebra.me][davide@poldebra.me]] or you
+can contact me through my
+[[https://www.linkedin.com/in/davidepolonio/][LinkedIn]] profile.
+
+** Why this website and this blog
+:PROPERTIES:
+:CUSTOM_ID: why-this-website-and-this-blog
+:END:
+As I have already written at the beginning of [my first blog post]({{<
+ref “/hello-world.md” >}}) I liked the idea of a place where I can share
+my thoughts regarding technologies, personal experiences and what not.
+This blog is open source, and all my content is licensed under CC BY-SA
+4.0. The repository can be found either on
+[[https://git.poldebra.me/polpetta/bitdispenser.dev][my personal Gitea instance]]
+or on
+[[https://github.com/Polpetta/bitdispenser.dev][Github (mirrored)]].
diff --git a/org/index.org b/org/index.org
new file mode 100644
index 0000000..2c44181
--- /dev/null
+++ b/org/index.org
@@ -0,0 +1,6 @@
+#+TITLE: Bitdispenser homepage
+#+AUTHOR: Davide Polonio
+
+Welcome
+
+cmdfklvdfm
diff --git a/static/content/hello-world/engiwithdispenser.png b/org/media/hello-world/engiwithdispenser.png
similarity index 100%
rename from static/content/hello-world/engiwithdispenser.png
rename to org/media/hello-world/engiwithdispenser.png
diff --git a/static/content/hello-world/fleek.png b/org/media/hello-world/fleek.png
similarity index 100%
rename from static/content/hello-world/fleek.png
rename to org/media/hello-world/fleek.png
diff --git a/static/content/songlify/telegramscreen.png b/org/media/songlify/telegramscreen.png
similarity index 100%
rename from static/content/songlify/telegramscreen.png
rename to org/media/songlify/telegramscreen.png
diff --git a/org/posts/hello-world.org b/org/posts/hello-world.org
new file mode 100644
index 0000000..c064903
--- /dev/null
+++ b/org/posts/hello-world.org
@@ -0,0 +1,166 @@
+If you are reading this it means either you are trying to understand who
+I am (are you a recruiter by any chance?) or you are simply bored. In
+any way, this is the very first blog post, and it means you have come to
+the end of the road (since this is the beginning). I always loved to
+have a place where I can write down my thoughts about a new technology,
+a framework or what I did in order to achieve a goal in a hobby project.
+That is not all though: I was searching for a place where I was also
+able to express my ideas regarding modern dilemmas like privacy issues
+and decentralization. I am not the kind of guy who likes social
+networks, so I never had the chance to express them. Until now.
+
+** How this blog is built
+:PROPERTIES:
+:CUSTOM_ID: how-this-blog-is-built
+:END:
+*** The website
+:PROPERTIES:
+:CUSTOM_ID: the-website
+:END:
+This blog is a very simple [[https://gohugo.io/][Hugo]] website. If you
+do not know what it is, its basically a static website generator. It
+takes Markdown documents and converts them in HTML pages. If you applied
+a specific theme then it builds the page according to that theme. Hugo
+has a huge selection of themes in its
+[[https://themes.gohugo.io/][dedicated themes page]], so basically
+picking up one and starting from there is very simple. If you are
+curious about how I made it, you can check out the source at my
+[[https://git.poldebra.me/polpetta/bitdispenser.dev][personal git server instance]],
+where I started hosting my code when
+[[https://news.microsoft.com/announcement/microsoft-acquires-github/][Microsoft bought Github]]
+(you can find a copy of the repository on Github too, visiting
+[[https://github.com/Polpetta][my profile]]). I have simply picked up
+the simpler and cleanest blog theme out there, following Ludwig Mies van
+der Rohe's idea:
+
+#+begin_quote
+less is more
+#+end_quote
+
+It will be a success if more than two people actually starts reading
+what I write here, at least I want them to read this blog without having
+their eyes bleeding with an extravagant color combination.
+
+Last but not least, the website icon is literally the Team Fortress 2
+dispenser (one of the games I love and one of the first game I started
+playing online as a kid when a decent connection at home was finally
+available), taken from the RED team and generated via a
+[[https://realfavicongenerator.net/][favicon generator]]. The name of
+the website partially came out from that, and from the fact that
+basically every website is a dispenser of bits, and it is only thanks to
+our beloved browsers we are able to actually “consume” what is
+distributed in the first place.
+
+#+CAPTION: Engineer class with a dispenser. Thanks to [[https://wiki.teamfortress.com][Team Fortress Wiki]] for providing the image I shamelessly downloaded from them
+#+NAME: fig:Engineer with his dispenser
+[[../media/hello-world/engiwithdispenser.png]]
+
+I hope Valve will not sue me for taking that asset as my website
+favicon. I swear I will change it, a day. Pinky promise.
+
+*** Hosting
+:PROPERTIES:
+:CUSTOM_ID: hosting
+:END:
+The real problem of hosting a website now day is not how to build it (as
+you can see) but /where/ you can host it. There are plenty of cloud
+provides: AWS, Google Cloud, DigitalOcean, Scaleway, etc... every day
+there is a new one popping up. They all offers the possibility to host
+your website pretty easily, especially if the website is a static one
+like this (e.g. using a S3 bucket). Since I like challenges and I also
+like to learn new stuff, I though that hosting the website in this way
+was boring. At the same time, I wanted to have a good uptime and to not
+meddle too much under the hood. My (dream) requirements were:
+
+- always up
+- good response time
+- possibility to host as much data as I want
+- using a hosting free as in beer and (possibly) that could use free as
+ in freedom technologies
+
+Now, reading this I imagine you are thinking I am going crazy, and maybe
+I am, but that is not the case. In fact, multiple weeks prior to writing
+this article, while trying to kill the boredom caused by COVID-19
+lockdown, I discovered [[https://ipfs.io/][IPFS]]. I already heard of it
+at University, but I never bothered too much to understand what was
+about. I though “well, it surely is some sort of filesystem”. I was
+somewhat right, but not the way I thought.
+
+IPFS acts like a peer-to-peer network, where nodes hash the content they
+want to share to let other nodes grab it. You can grab this content
+using your local node or using one of the many available gateways. Nodes
+can “pin” a file too, in order to keep it locally and to serve it to
+other nodes. If a file gets pinned by different nodes and gains traction
+it basically can not be deleted from the web.
+
+**** Choosing the right tools
+:PROPERTIES:
+:CUSTOM_ID: choosing-the-right-tools
+:END:
+Surely, as
+[[https://docs.ipfs.io/how-to/websites-on-ipfs/multipage-website/][the IPFS documentation describes]],
+I could have done it by myself. But there is a but. Holding all the
+infrastructure manually means sacrifice the “always up” and “good
+response time” thingy for two simple motives:
+
+1. the
+ [[https://git.poldebra.me][server where I host my drafts & codebase]]
+ is hosted on a very small machine (2GB of RAM and 2vCPU), very easy
+ to kill with the slightest of loads
+2. in order to achieve a good response time, a CDN or some sort of
+ caching is necessary (even if the application is stored in a
+ distributed file system)
+
+
+#+CAPTION: Fleek website, that I used to automate my deployment on IPFS and my DNS update
+#+NAME: fig:The fleek website screenshot
+#+ATTR_HTML: :width 600px
+[[../media/hello-world/fleek.png]]
+
+Finally, in order to achieve full automation with DNS updates, I would
+have needed to implement and use NameCheap APIs (currently it is the DNS
+provider I use for most of my websites). “What's the difficulty?” one
+would ask.
+[[https://www.namecheap.com/support/api/intro/][Here is the official documentation]],
+and even if the APIs look promising, while studying them my will to live
+decreased a little bit, and so I decided that if I wanted to get up and
+running with less maintenance as possible, with a good uptime while
+having the maximum automation possible I needed to rely on a dedicated
+service. Luckily [[https://fleek.co][fleek.co]] was what I was searching
+for. They currently provide the possibility to buy a domain from their
+website, giving them all the hassle of updating the new website on IPFS,
+distributing it, refreshing a very possible CDN and finally to update
+the DNS records accordingly. This, as you can imagine, provides multiple
+benefits:
+
+- I do not have to care about my very little dev machine getting hugged
+ to death by request in the remote case any of my posts get any
+ attention
+- I do not have to focus on automating the process someone else has
+ already done for me
+- I can focus on writing posts after dinner instead of scratching my
+ head trying to understand why the website does not load/the DNS is not
+ properly updated
+
+The only downside to this approach is that Fleek provides limitations on
+how much data and bandwidth you can host. At the time of writing, you
+can only host up to 3GB (enough for this website) and have a 50GB
+bandwidth (that is fine for now) for the free version.
+[[https://fleek.co/pricing/][Upgrading you account]] to one of the
+available plans give you extra space and bandwidth.
+
+** Future improvements
+:PROPERTIES:
+:CUSTOM_ID: future-improvements
+:END:
+For sure, this is only the beginning. Having an automatic workflow of
+spell check, deployment and release would be the first milestone. Future
+features for this website could be an automatic posting of every new
+article on a dedicated Mastodon bot, so that people can possibly discuss
+about my thoughts on the fediverse, a decentralized network.
+
+To conclude, the frequency of this blog will be...whenever I have time
+to post :grin: Of course I need content before posting something, and
+this require some time for myself for experimenting with new
+technologies and learning new stuff, so I do not expect very much
+posting, but only time will tell!
diff --git a/org/posts/songlify-1.org b/org/posts/songlify-1.org
new file mode 100644
index 0000000..9733cf2
--- /dev/null
+++ b/org/posts/songlify-1.org
@@ -0,0 +1,272 @@
+Some time passed since the last article I wrote there. A lot of stuff
+happened meanwhile, especially with COVID, but here we are again. While
+busy dealing with the mess of real life tasks, three months ago I
+started to write a little bot for Telegram in Rust. It is a simple one,
+but I consider the journey interesting and worth of writing it down. If
+I add new features worth mentioning I will start a series about it,
+maybe.
+
+** Telegram bots
+:PROPERTIES:
+:CUSTOM_ID: telegram-bots
+:END:
+Telegram bots are not something new to me and nowadays are pretty much
+easy to make, so I consider them like a gym where to try out new
+technologies and experiment with stuff. I wrote plentiful of them, some
+of those are open source like for example
+[[https://github.com/Augugrumi/TorreArchimedeBot]] (which is currently
+broken 😭) that was useful when going to University, because it scraped
+the university free room web page and from there it was able to tell you
+which rooms where without any lessons and for how much time, allowing
+you to easily find a place where to study with your mates (yep, we
+didn't like library too much).
+
+
+#+CAPTION: A screenshot of TorreArchimedeBot in action.
+#+NAME: fig:A screenshot of TorreArchimedeBot in action.
+#+ATTR_HTML: :width 600px
+[[../media/songlify/telegramscreen.png]]
+
+Also another one bot worthy of mention is
+[[https://github.com/Polpetta/RedditToTelegram]], that allowed our D&D
+group to receive push notifications of our private Subreddit in our
+Telegram group.
+
+As you can see, all of these bots are quite simple, but they have the
+added value of teaching you some new programming concepts, technologies
+or frameworks that can be later applied in something that can be more
+production environment.
+
+** Rust
+:PROPERTIES:
+:CUSTOM_ID: rust
+:END:
+I started to approach Rust many years ago (I do not remember exactly
+when). First interaction with it was quite interesting to say at least:
+there were way less compiler features (for example now the compiler is
+able to understand object lifetime at compile time most of the time
+alone, without specifying them) that made it a... /not-so-pleasant
+programming experience/. It had potential thought, so by following Rust
+news I picked it up last year again, noticing that now it has improved a
+lot and it is more pleasant to write. Meanwhile, also JetBrains
+developed a good support for IntelliJ, so now it is even possible to
+debug and perform every operation directly from your IDE UI.
+
+** Making the two worlds collide: Rust + Telegram
+:PROPERTIES:
+:CUSTOM_ID: making-the-two-worlds-collide-rust-telegram
+:END:
+One of the features I wanted to learn this time regarding Rust was the
+asynchronous support it offers. Rust started to have =async= support
+with [[https://tokio.rs/][Tokio]] framework, and recently the Rust team
+started to build the asynchronous functionality inside Rust itself. Even
+if in the first steps, it looks promising and the idea of a low-level
+language, without GC, with automatic memory management and so much
+safety having asynchronous support is exciting to me! 🥳 So the only
+option left, at this point, was to start messing around with it. I
+started by picking up one of the many frameworks that provides a layer
+for the Telegram APIs,
+[[https://github.com/teloxide/teloxide][Teloxide]]. In particular, as
+you can see from its /README/, one of the examples starts by using
+=#[tokio:main]= macro:
+
+#+begin_src rust
+use teloxide::prelude::*;
+
+#[tokio::main]
+async fn main() {
+ teloxide::enable_logging!();
+ log::info!("Starting dices_bot...");
+
+ let bot = Bot::from_env().auto_send();
+
+ teloxide::repl(bot, |message| async move {
+ message.answer_dice().await?;
+ respond(())
+ })
+ .await;
+}
+#+end_src
+
+This was the reason I picked it up, given that it looked the most
+promising by the time I started the project.
+
+*** Building /Songlify/
+:PROPERTIES:
+:CUSTOM_ID: building-songlify
+:END:
+So, after choosing what was going to use to build the bot, I needed a
+/reason/ to build it.
+
+With my friends we usually share a lot of songs (via Spotify links), so
+I thought it was a good idea to build a bot around it. I integrated a
+Spotify API library in it and started hacking up a bot.
+
+#+begin_quote
+⚠ Note that at the time of writing I have just noticed that the library
+I use for speaking with Spotify,
+[[https://crates.io/crates/aspotify][aspotify]] has been deprecated in
+favour of [[https://crates.io/crates/rspotify][rspotify]]
+#+end_quote
+
+The first bot version was something very simple, and it was a
+single-file program with nothing very fancy (I have written it in a
+night):
+
+#+begin_src rust
+use crate::SpotifyURL::Track;
+use aspotify::{Client, ClientCredentials};
+use teloxide::prelude::*;
+
+enum SpotifyURL {
+ Track(String),
+}
+
+fn get_spotify_entry(url: &str) -> Option {
+ if url.contains("https://open.spotify.com/track/") {
+ let track_id = url.rsplit('/').next().and_then(|x| x.split('?').next());
+ return match track_id {
+ Some(id) => Some(SpotifyURL::Track(id.to_string())),
+ None => None,
+ };
+ }
+ return None;
+}
+
+struct TrackInfo {
+ name: String,
+ artist: Vec,
+}
+
+async fn get_spotify_track(spotify: Box, id: &String) -> Option {
+ match spotify.tracks().get_track(id.as_str(), None).await {
+ Ok(track) => Some(TrackInfo {
+ name: track.data.name,
+ artist: track.data.artists.iter().map(|x| x.name.clone()).collect(),
+ }),
+ Err(_e) => None,
+ }
+}
+
+#[tokio::main]
+async fn main() {
+ teloxide::enable_logging!();
+ log::info!("Starting Songlify...");
+
+ let bot = Bot::from_env().auto_send();
+ teloxide::repl(bot, |message| async move {
+ let spotify_creds =
+ ClientCredentials::from_env().expect("CLIENT_ID and CLIENT_SECRET not found.");
+ let spotify_client = Box::new(Client::new(spotify_creds));
+
+ log::info!("Connected to Spotify");
+ let text = message.update.text().and_then(get_spotify_entry);
+ match text {
+ Some(spotify) => match spotify {
+ Track(id) => {
+ let track_info = get_spotify_track(spotify_client, &id).await;
+ match track_info {
+ Some(info) => {
+ let reply = format!(
+ "Track information:\n\
+ Track name: {}\n\
+ Artists: {}",
+ info.name,
+ info.artist.join(", ")
+ );
+ Some(message.reply_to(reply).await?)
+ }
+ None => None,
+ }
+ }
+ },
+ None => None,
+ };
+ respond(())
+ })
+ .await;
+
+ log::info!("Exiting...");
+}
+#+end_src
+
+As you can see, basically every time a request arrived to the bot, login
+to Spotify was performed and track information and name retrieved from
+there. Of course this was only the beginning (also you can “appreciate”
+the number of nested blocks there...). Now the bot supports albums and
+playlists too, with the possibility to go through each song in the
+playlist and collect general information such as how many artists are in
+that playlist, how many songs and other little information like that. If
+you see the
+[[https://git.poldebra.me/polpetta/Songlify][bot repository]] you can
+see now that Spotify functions live in a separate module.
+
+**** Packaging and distribution
+:PROPERTIES:
+:CUSTOM_ID: packaging-and-distribution
+:END:
+The obvious choice for a software like that was to incorporate it into a
+OCI image. I wrote a very simple Dockerfile that, once the program was
+built, took the artifact and using the
+[[https://docs.docker.com/develop/develop-images/multistage-build/][multi-stage Docker build functionality]]
+and put it into a separate container, in order to avoid having build
+dependencies inside the final image. I used the images distributed by
+the [[file:gcr.io/distroless/][Distroless project]] (you can find the
+source on their
+[[https://github.com/GoogleContainerTools/distroless][Github repository]])
+in order to obtain the smallest possible image. The final result?
+
+#+begin_src txt
+λ ~/Desktop/git/songlify/ docker images
+REPOSITORY TAG IMAGE ID CREATED SIZE
+test/test latest 8ac7a7018719 5 seconds ago 34MB
+ 4bc7fb0699e0 12 seconds ago 1.53GB
+rust 1.56.1-slim-bullseye d3e070c5ffa7 6 weeks ago 667MB
+gcr.io/distroless/base latest-amd64 24787c1cd2e4 52 years ago 20.2MB
+#+end_src
+
+A part from the 52 years old image pulled from =gcr=, you can see that
+=test/test= (actually Songlify) is only of *34MB*. Not much if you
+consider that inside that image there are shared dynamic libraries to
+make the executable able to run, which by default weights 20MB. A plus
+of these images is that they do not run as root user and they do not
+have any shell of bash integrated, making a possible surface attack
+smaller (not that Docker is secure anyway...). I upload the images on
+Docker Hub, where you can find them here
+[[https://hub.docker.com/r/polpetta/songlify]]
+
+Finally, to run the bot I use a very simple docker-compose definition,
+that can be found in my
+[[https://git.poldebra.me/polpetta/server-dotfiles/src/commit/7e7e1780b2db45f475510c49bf1a2f9e76c4c166/songlify/docker-compose.yml][server-dotfiles repository]].
+This allows me to easily upgrade the bot by just changing the version
+and running =docker-compose up -d=.
+
+**** Plans for the future
+:PROPERTIES:
+:CUSTOM_ID: plans-for-the-future
+:END:
+Currently I work on the bot only when I feel like I wanna add something.
+An interesting feature to add could be to insert a persistence layer
+(using a database for instance) and add various stats (which is the most
+shared song? Who /is that guy/ that shares the most songs in a group?).
+Persistence can be achieved quite easily by using
+[[https://diesel.rs/][Diesel]], an ORM compatible with various
+databases.
+
+Another cool feature could be to add the /inline bot/ functionality,
+where you can search for songs directly in Spotify. Since I currently
+have a domain available for that I could set it up for receive web-hook
+notifications, instead of performing polling like the bot is currently
+doing (one requisite for inline bots is indeed to receive web-hooks).
+There are platforms like Heroku where you could make the bot run, but
+currently I prefer to use my box since it gives me more flexibility.
+Experimenting with Heroku could lead to cool results though 😏.
+
+Finally, link translation could be something very useful. I have a
+friend that does not use Spotify but prefers to listen to music via
+YouTube. So an interesting feature would be, given a Spotify link, to
+“convert” it into a YouTube link and, of course, /vice-versa/. This
+could lead to translate playlists and albums too into YouTube-based
+playlists, which of course could be very useful if you are trying to
+avoid the infamous /vendor lock-in/, in this case being stuck with
+Spotify because all you music collection, saved songs, etc is there.
diff --git a/org/rss.org b/org/rss.org
new file mode 100644
index 0000000..e69de29
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index 18e6c8f..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,739 +0,0 @@
-{
- "name": "bitdispenser.dev",
- "version": "0.0.1",
- "lockfileVersion": 2,
- "requires": true,
- "packages": {
- "": {
- "name": "bitdispenser.dev",
- "version": "0.0.1",
- "license": "CC-BY-SA-4.0",
- "devDependencies": {
- "markdownlint-cli2": "0.0.10"
- }
- },
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
- "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==",
- "dev": true,
- "dependencies": {
- "@nodelib/fs.stat": "2.0.4",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz",
- "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==",
- "dev": true,
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz",
- "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==",
- "dev": true,
- "dependencies": {
- "@nodelib/fs.scandir": "2.1.4",
- "fastq": "^1.6.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "dev": true,
- "dependencies": {
- "sprintf-js": "~1.0.2"
- }
- },
- "node_modules/array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "dependencies": {
- "fill-range": "^7.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/dir-glob": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
- "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
- "dev": true,
- "dependencies": {
- "path-type": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/entities": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
- "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==",
- "dev": true
- },
- "node_modules/fast-glob": {
- "version": "3.2.5",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
- "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==",
- "dev": true,
- "dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.0",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.2",
- "picomatch": "^2.2.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/fastq": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz",
- "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==",
- "dev": true,
- "dependencies": {
- "reusify": "^1.0.4"
- }
- },
- "node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/globby": {
- "version": "11.0.3",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz",
- "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==",
- "dev": true,
- "dependencies": {
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.1.1",
- "ignore": "^5.1.4",
- "merge2": "^1.3.0",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ignore": {
- "version": "5.1.8",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
- "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
- "dev": true,
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
- "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
- "dev": true,
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/linkify-it": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz",
- "integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==",
- "dev": true,
- "dependencies": {
- "uc.micro": "^1.0.1"
- }
- },
- "node_modules/markdown-it": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.0.tgz",
- "integrity": "sha512-+CvOnmbSubmQFSA9dKz1BRiaSMV7rhexl3sngKqFyXSagoA3fBdJQ8oZWtRy2knXdpDXaBw44euz37DeJQ9asg==",
- "dev": true,
- "dependencies": {
- "argparse": "^1.0.7",
- "entities": "~2.0.0",
- "linkify-it": "^3.0.1",
- "mdurl": "^1.0.1",
- "uc.micro": "^1.0.5"
- },
- "bin": {
- "markdown-it": "bin/markdown-it.js"
- }
- },
- "node_modules/markdownlint": {
- "version": "0.21.1",
- "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.21.1.tgz",
- "integrity": "sha512-8kc88w5dyEzlmOWIElp8J17qBgzouOQfJ0LhCcpBFrwgyYK6JTKvILsk4FCEkiNqHkTxwxopT2RS2DYb/10qqg==",
- "dev": true,
- "dependencies": {
- "markdown-it": "11.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/markdownlint-cli2": {
- "version": "0.0.10",
- "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.0.10.tgz",
- "integrity": "sha512-tuWKncGgg0ic9YIWzPu1w2upoGNF/CQwb+CMzJmB8LMM2MhFKTUQSDyUt0kM022qs1R7KjxFXG7y5FBmqGVhUQ==",
- "dev": true,
- "dependencies": {
- "globby": "~11.0.1",
- "markdownlint": "~0.21.0",
- "markdownlint-cli2-formatter-default": "~0.0.1",
- "markdownlint-rule-helpers": "~0.12.0",
- "micromatch": "~4.0.2",
- "strip-json-comments": "~3.1.1",
- "yaml": "~1.10.0"
- },
- "bin": {
- "markdownlint-cli2": "markdownlint-cli2.js"
- },
- "engines": {
- "node": ">=10.17.0"
- }
- },
- "node_modules/markdownlint-cli2-formatter-default": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.2.tgz",
- "integrity": "sha512-jIz1X3SIC8sX4NDFqQFUXL+JEtfnDoN4i+xocEu+etcxGX455pHb6sx86f/yVk4mKJ2o7aNe2ydSx9an22BfBg==",
- "dev": true,
- "peerDependencies": {
- "markdownlint-cli2": ">=0.0.4"
- }
- },
- "node_modules/markdownlint-rule-helpers": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.12.0.tgz",
- "integrity": "sha512-Q7qfAk+AJvx82ZY52OByC4yjoQYryOZt6D8TKrZJIwCfhZvcj8vCQNuwDqILushtDBTvGFmUPq+uhOb1KIMi6A==",
- "dev": true
- },
- "node_modules/mdurl": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
- "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
- "dev": true
- },
- "node_modules/merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/micromatch": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
- "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
- "dev": true,
- "dependencies": {
- "braces": "^3.0.1",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
- "node_modules/path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/picomatch": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz",
- "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==",
- "dev": true,
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ]
- },
- "node_modules/reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
- "dev": true,
- "engines": {
- "iojs": ">=1.0.0",
- "node": ">=0.10.0"
- }
- },
- "node_modules/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "dependencies": {
- "queue-microtask": "^1.2.2"
- }
- },
- "node_modules/slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
- "dev": true
- },
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/uc.micro": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
- "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
- "dev": true
- },
- "node_modules/yaml": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
- "dev": true,
- "engines": {
- "node": ">= 6"
- }
- }
- },
- "dependencies": {
- "@nodelib/fs.scandir": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
- "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==",
- "dev": true,
- "requires": {
- "@nodelib/fs.stat": "2.0.4",
- "run-parallel": "^1.1.9"
- }
- },
- "@nodelib/fs.stat": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz",
- "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==",
- "dev": true
- },
- "@nodelib/fs.walk": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz",
- "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==",
- "dev": true,
- "requires": {
- "@nodelib/fs.scandir": "2.1.4",
- "fastq": "^1.6.0"
- }
- },
- "argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "dev": true,
- "requires": {
- "sprintf-js": "~1.0.2"
- }
- },
- "array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "dev": true
- },
- "braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "requires": {
- "fill-range": "^7.0.1"
- }
- },
- "dir-glob": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
- "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
- "dev": true,
- "requires": {
- "path-type": "^4.0.0"
- }
- },
- "entities": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
- "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==",
- "dev": true
- },
- "fast-glob": {
- "version": "3.2.5",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
- "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==",
- "dev": true,
- "requires": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.0",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.2",
- "picomatch": "^2.2.1"
- }
- },
- "fastq": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz",
- "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==",
- "dev": true,
- "requires": {
- "reusify": "^1.0.4"
- }
- },
- "fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- },
- "glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "requires": {
- "is-glob": "^4.0.1"
- }
- },
- "globby": {
- "version": "11.0.3",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz",
- "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==",
- "dev": true,
- "requires": {
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.1.1",
- "ignore": "^5.1.4",
- "merge2": "^1.3.0",
- "slash": "^3.0.0"
- }
- },
- "ignore": {
- "version": "5.1.8",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
- "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
- "dev": true
- },
- "is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "dev": true
- },
- "is-glob": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
- "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
- "dev": true,
- "requires": {
- "is-extglob": "^2.1.1"
- }
- },
- "is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
- },
- "linkify-it": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz",
- "integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==",
- "dev": true,
- "requires": {
- "uc.micro": "^1.0.1"
- }
- },
- "markdown-it": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.0.tgz",
- "integrity": "sha512-+CvOnmbSubmQFSA9dKz1BRiaSMV7rhexl3sngKqFyXSagoA3fBdJQ8oZWtRy2knXdpDXaBw44euz37DeJQ9asg==",
- "dev": true,
- "requires": {
- "argparse": "^1.0.7",
- "entities": "~2.0.0",
- "linkify-it": "^3.0.1",
- "mdurl": "^1.0.1",
- "uc.micro": "^1.0.5"
- }
- },
- "markdownlint": {
- "version": "0.21.1",
- "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.21.1.tgz",
- "integrity": "sha512-8kc88w5dyEzlmOWIElp8J17qBgzouOQfJ0LhCcpBFrwgyYK6JTKvILsk4FCEkiNqHkTxwxopT2RS2DYb/10qqg==",
- "dev": true,
- "requires": {
- "markdown-it": "11.0.0"
- }
- },
- "markdownlint-cli2": {
- "version": "0.0.10",
- "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.0.10.tgz",
- "integrity": "sha512-tuWKncGgg0ic9YIWzPu1w2upoGNF/CQwb+CMzJmB8LMM2MhFKTUQSDyUt0kM022qs1R7KjxFXG7y5FBmqGVhUQ==",
- "dev": true,
- "requires": {
- "globby": "~11.0.1",
- "markdownlint": "~0.21.0",
- "markdownlint-cli2-formatter-default": "~0.0.1",
- "markdownlint-rule-helpers": "~0.12.0",
- "micromatch": "~4.0.2",
- "strip-json-comments": "~3.1.1",
- "yaml": "~1.10.0"
- }
- },
- "markdownlint-cli2-formatter-default": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.2.tgz",
- "integrity": "sha512-jIz1X3SIC8sX4NDFqQFUXL+JEtfnDoN4i+xocEu+etcxGX455pHb6sx86f/yVk4mKJ2o7aNe2ydSx9an22BfBg==",
- "dev": true,
- "requires": {}
- },
- "markdownlint-rule-helpers": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.12.0.tgz",
- "integrity": "sha512-Q7qfAk+AJvx82ZY52OByC4yjoQYryOZt6D8TKrZJIwCfhZvcj8vCQNuwDqILushtDBTvGFmUPq+uhOb1KIMi6A==",
- "dev": true
- },
- "mdurl": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
- "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
- "dev": true
- },
- "merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true
- },
- "micromatch": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
- "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
- "dev": true,
- "requires": {
- "braces": "^3.0.1",
- "picomatch": "^2.2.3"
- }
- },
- "path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
- "dev": true
- },
- "picomatch": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz",
- "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==",
- "dev": true
- },
- "queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true
- },
- "reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
- "dev": true
- },
- "run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "requires": {
- "queue-microtask": "^1.2.2"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true
- },
- "sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
- "dev": true
- },
- "strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true
- },
- "to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "requires": {
- "is-number": "^7.0.0"
- }
- },
- "uc.micro": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
- "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
- "dev": true
- },
- "yaml": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
- "dev": true
- }
- }
-}
diff --git a/package.json b/package.json
deleted file mode 100644
index 4ad5974..0000000
--- a/package.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "name": "bitdispenser.dev",
- "version": "0.0.1",
- "description": "Personal Hugo website",
- "main": "none.js",
- "scripts": {
- "lint": "markdownlint-cli2 content/",
- "spellchecker": "bash tools/spellchecker.sh en"
- },
- "repository": {
- "type": "git",
- "url": "git+https://git.poldebra.me/polpetta/bitdispenser.dev.git"
- },
- "author": "Polonio Davide ",
- "license": "CC-BY-SA-4.0",
- "devDependencies": {
- "markdownlint-cli2": "0.0.10"
- }
-}
diff --git a/publish.el b/publish.el
new file mode 100755
index 0000000..6c666ac
--- /dev/null
+++ b/publish.el
@@ -0,0 +1,80 @@
+#!/usr/bin/env -S emacs -x
+
+(require 'package)
+(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
+(package-initialize)
+
+;; Install ox-rss if not present
+(unless (package-installed-p 'ox-rss)
+ (package-refresh-contents)
+ (package-install 'ox-rss))
+
+(require 'ox-publish)
+(require 'ox-rss)
+(setq org-publish-project-alist
+ '(("blog-posts"
+ :base-directory "org/posts/"
+ :base-extension "org"
+ :publishing-directory "dist/p/"
+ :recursive t
+ :publishing-function org-html-publish-to-html
+ :org-html-preamble nil
+ :html-self-link-headlines nil
+ :org-export-with-title nil
+ :org-export-with-toc nil
+ :org-export-with-section-numbers nil
+ :org-export-with-properties nil
+ :org-export-with-tags nil
+ :org-export-with-date t
+ :org-export-with-time-stamp-file t
+ :org-export-with-tables t
+ :org-html-table-use-header-tags-for-first-column nil
+ )
+ ("blog-pages"
+ :base-directory "org/"
+ :base-extension "org"
+ :publishing-directory "dist/"
+ :exclude "posts/\\|rss\\.org"
+ :recursive t
+ :publishing-function org-html-publish-to-html
+ :org-html-preamble nil
+ :html-self-link-headlines nil
+ :org-export-with-title nil
+ :org-export-with-toc nil
+ :org-export-with-section-numbers nil
+ :org-export-with-tags nil
+ :org-html-table-use-header-tags-for-first-column nil
+ )
+ ("blog-media"
+ :base-directory "org/media"
+ :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
+ :publishing-directory "dist/media/"
+ :recursive t
+ :publishing-function org-publish-attachment
+ )
+ ("blog-static"
+ :base-directory "static/"
+ :base-extension "*"
+ :publishing-directory "dist/media/"
+ :recursive nil
+ :publishing-function org-publish-attachment
+ )
+ ("blog-rss"
+ :base-directory "org/posts/"
+ :base-extension "org"
+ :recursive nil
+ :exclude ".*"
+ :include ("../rss.org")
+ :publishing-function (org-rss-publish-to-rss)
+ :publishing-directory "dist/"
+ :with-toc nil
+ :section-numbers nil
+ :html-link-use-abs-url t
+ :html-link-home "https://bitdispenser.dev/"
+ :title "Bitdispenser RSS"
+ )
+ ("blog"
+ :components ("blog-posts" "blog-pages" "blog-media" "blog-static" "blog-rss"))
+ ))
+
+(org-publish "blog" t)
diff --git a/templates/post.org b/templates/post.org
new file mode 100644
index 0000000..1f3198a
--- /dev/null
+++ b/templates/post.org
@@ -0,0 +1,7 @@
+#+options: html-link-use-abs-url:nil html-postamble:nil html-preamble:nil
+#+options: html-scripts:t html-style:nil html5-fancy:t tex:t
+#+options: tags:t title:nil toc:nil num:0 date:t
+#+html_doctype: html5
+#+html_container: div
+#+html_head:
+#+creator: Davide Polonio
diff --git a/themes/hermit b/themes/hermit
deleted file mode 160000
index 2dc35c5..0000000
--- a/themes/hermit
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 2dc35c5c6a52168a3a7b35c5ad51209f40a851cf