Watch
1
0
Fork
You've already forked www.xengi.de
0

initial drop

This commit is contained in:
Ricardo (XenGi) Band 2022-01-07 19:51:45 +01:00
commit a1c44e502d
No known key found for this signature in database
GPG key ID: 1A9D657D06B5820E
17 changed files with 1133 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
__pycache__
.idea
output

11
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,11 @@
image: python:slim
before_script:
- pip install -r requirements.txt
pages:
script:
- pelican content -s publishconf.py
artifacts:
paths:
- output

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "flex"]
path = flex
url = https://github.com/alexandrevicenzi/Flex.git

72
Makefile Normal file
View file

@ -0,0 +1,72 @@
PY?=python3
PELICAN?=pelican
PELICANOPTS=
BASEDIR=$(CURDIR)
INPUTDIR=$(BASEDIR)/content
OUTPUTDIR=$(BASEDIR)/output
CONFFILE=$(BASEDIR)/pelicanconf.py
PUBLISHCONF=$(BASEDIR)/publishconf.py
DEBUG ?= 0
ifeq ($(DEBUG), 1)
PELICANOPTS += -D
endif
RELATIVE ?= 0
ifeq ($(RELATIVE), 1)
PELICANOPTS += --relative-urls
endif
SERVER ?= "0.0.0.0"
PORT ?= 0
ifneq ($(PORT), 0)
PELICANOPTS += -p $(PORT)
endif
help:
@echo 'Makefile for a pelican Web site '
@echo ' '
@echo 'Usage: '
@echo ' make html (re)generate the web site '
@echo ' make clean remove the generated files '
@echo ' make regenerate regenerate files upon modification '
@echo ' make publish generate using production settings '
@echo ' make serve [PORT=8000] serve site at http://localhost:8000'
@echo ' make serve-global [SERVER=0.0.0.0] serve (as root) to $(SERVER):80 '
@echo ' make devserver [PORT=8000] serve and regenerate together '
@echo ' make devserver-global regenerate and serve on 0.0.0.0 '
@echo ' '
@echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html '
@echo 'Set the RELATIVE variable to 1 to enable relative urls '
@echo ' '
html:
"$(PELICAN)" "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS)
clean:
[ ! -d "$(OUTPUTDIR)" ] || rm -rf "$(OUTPUTDIR)"
regenerate:
"$(PELICAN)" -r "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS)
serve:
"$(PELICAN)" -l "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS)
serve-global:
"$(PELICAN)" -l "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -b $(SERVER)
devserver:
"$(PELICAN)" -lr "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS)
devserver-global:
$(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -b 0.0.0.0
publish:
"$(PELICAN)" "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(PUBLISHCONF)" $(PELICANOPTS)
.PHONY: html help clean regenerate serve serve-global devserver publish

BIN
content/images/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
content/images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -0,0 +1,529 @@
title: Arch Linux install guide 2022 Edition
date: 2021-12-24
tags: arch linux
keywords: arch linux
category: linux
summary: This is how I setup Arch Linux in 2022
# Introduction
This is the way I usually install Arch Linux. It is heavily based on systemd services to avoid the installation of additional software. Use it as inspiration for your own custom install.
Arch Linux is all about freedom of choice so these are the choices I made:
| Component | Implementation |
| ---------------- | ----------------------------- |
| Kernel | Default `linux` |
| Partition layout | GPT |
| Bootloader | `systemd-boot` |
| Initramfs | `systemd` based `mkinitcpio` |
| Partitioning | Primary partitions, single partition for root and home |
| Encryption | LUKS2 for swap and rootfs |
| Root filesystem | Ext4 |
| Swap space | Dedicated partition |
| Network manager | `systemd-networkd` |
| Wifi | `wpa_supplicant` |
| Mobile broadband | Custom `modem-manager` script |
| Name resolution | `systemd-resolved` |
| Network Time | `systemd-timesyncd` |
| Text Editor | Neovim (`nvim`) |
| User shell | `fish` |
| `/bin/sh` Shell | `dash` |
| Privilege excalation | `sudo` |
| Desktop Environment | `xfce` |
| Window Manager | `i3` |
| Terminal | `xfce-terminal` |
| Keyboard layout | en-us international with AtlGr dead keys; caps lock as compose key |
Explanation for code examples:
```
# command executed as root
$ command executed as unprivileged user
% command executed inside chroot as root
```
## Assumptions
- You install to a notebook or desktop computer (ignore battery related things)
- You install to an NVME drive
- You read through my commands carefully and understand what they do before blindly pasting them into your machine
- I'm not responsible for anything you do
__With that out of the way, let's get started!__
# Installation
## Partition disks
Create 3 primary partitions for:
- ESP: 1GB, FAT32
- Swap:
- With hibernation support: same as RAM size
- Without: ~2-8GB depending on the use case of the machine
- RootFS: Rest of your disk
```
# cfdisk -z /dev/nvme0n1
# mkdosfs -F 32 -n ESP /dev/nvme0n1p1
# cryptsetup LuksFormat --type luks2 --label CRYPTSWAP /dev/nvme0n1p2
# cryptsetup LuksFormat --type luks2 --label CRYPTROOT /dev/nvme0n1p3
# cryptsetup open --allow-discards --perf-no_read_workqueue --perf-no_write_workqueue --persistent --type luks2 /dev/nvme0n1p2 cryptswap
# cryptsetup open --allow-discards --perf-no_read_workqueue --perf-no_write_workqueue --persistent --type luks2 /dev/nvme0n1p3 cryptroot
# mkswap -L SWAP /dev/mapper/cryptswap
# mkfs.ext4 -L ROOT -O metadata_csum /dev/mapper/cryptroot
# mount /dev/mapper/cryptroot /mnt -o noatime,discard
# mkdir /mnt/boot
# mount /dev/nvme0n1p1 /mnt/boot -o noatime,discard
# swapon -d /dev/mapper/cryptswap
```
Final result should look like this:
```
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 953.9G 0 disk
├─nvme0n1p1 259:1 0 1G 0 part /boot
├─nvme0n1p2 259:2 0 32G 0 part
│ └─cryptswap 254:1 0 32G 0 crypt [SWAP]
└─nvme0n1p3 259:3 0 920.9G 0 part
└─cryptroot 254:0 0 920.9G 0 crypt /
```
## Install base system
Some packages mentioned here depend on your hardware
```
# pacstrap /mnt base base-devel linux linux-firmware (intel-ucode|amd-ucode) [sof-firmware] neovim man-db man-pages texinfo
```
Generate fstab
```
# genfstab -U /mnt >> /mnt/etc/fstab
```
## Setup bootloader
Get UUIDs of drives:
```
# blkid
/dev/nvme0n1p1: LABEL_FATBOOT="ESP" LABEL="ESP" UUID="1234-0001" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="deadbeef-1234-5678-90ab-10000001"
/dev/nvme0n1p2: UUID="deadbeef-1234-5678-90ab-00000002" LABEL="CRYPTSWAP" TYPE="crypto_LUKS" PARTUUID="deadbeef-1234-5678-90ab-10000002"
/dev/nvme0n1p3: UUID="deadbeef-1234-5678-90ab-00000003" LABEL="CRYPTROOT" TYPE="crypto_LUKS" PARTUUID="deadbeef-1234-5678-90ab-20000003"
/dev/mapper/cryptswap: LABEL="SWAP" UUID="deadbeef-1234-5678-90ab-20000002" TYPE="swap"
/dev/mapper/cryptroot: LABEL="ROOT" UUID="deadbeef-1234-5678-90ab-20000003" BLOCK_SIZE="4096" TYPE="ext4"
```
Install and configure `systemd-boot`:
```
# arch-chroot /mnt # we'll stay in chroot for a while so I'll not prefix every code block with that
% bootctl install
% nvim /boot/loader/loader.conf
default arch.conf
timeout 3
console-mode keep
editor yes
auto-entries
auto-firmware
```
Setup boot entries:
```
% nvim /boot/loader/entries/arch.conf
title Arch Linux
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options root="UUID=deadbeef-1234-5678-90ab-20000003" resume="UUID=deadbeef-1234-5678-90ab-20000002" rd.luks.name=deadbeef-1234-5678-90ab-00000003=cryptroot rd.luks.name=deadbeef-1234-5678-90ab-00000002=cryptswap rd.luks.options=discard rw
% nvim /boot/loader/entries/arch-fallback.conf
title Arch Linux (fallback initramfs)
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux-fallback.img
options root="UUID=deadbeef-1234-5678-90ab-20000003" resume="UUID=deadbeef-1234-5678-90ab-20000002" rd.luks.name=deadbeef-1234-5678-90ab-00000003=cryptroot rd.luks.name=deadbeef-1234-5678-90ab-00000002=cryptswap rd.luks.options=discard rw
```
## Setup vconsole
```
% nvim /etc/vconsole.conf
KEYMAP=us
FONT=lat2-16
FONT_MAP=8859-2
```
## Setup initramfs
Switching to `systemd` based initramfs:
```
% nvim /etc/mkinitcpio.conf
[...]
HOOKS=(base systemd autodetect keyboard sd-vconsole modconf block sd-encrypt filesystems fsck)
[...]
% mkinitcpio -P
```
## Setup networking
Setup for wired connections:
```
% nvim /etc/systemd/network/wired.network
[Match]
Name=enp*
[Network]
DHCP=yes
MulticastDNS=resolve
DNSSEC=allow-downgrade
[DHCP]
RouteMetric=10
```
Setup for wireless connections:
```
% nvim /etc/systemd/network/wireless.network
[Match]
Name=wlp*
[Network]
DHCP=yes
MulticastDNS=resolve
DNSSEC=allow-downgrade
IPv6PrivacyExtensions=kernel
[DHCP]
RouteMetric=20
```
Enable IPv6 privacy extensions:
Execute this first:
```
% sysctl net.ipv6.conf.wlp3s0.addr_gen_mode=3
% ip link set wlp3s0 down
% ip link set wlp3s0 up
% sysctl net.ipv6.conf.wlp3s0.stable_secret
```
Take the secret from the last command and put it here:
```
% nvim /etc/sysctl.d/40-ipv6.conf
# Enable IPv6 Privacy Extensions
net.ipv6.conf.all.use_tempaddr = 2
net.ipv6.conf.default.use_tempaddr = 2
net.ipv6.conf.wlp3s0.use_tempaddr = 2
net.ipv6.conf.enp5s0.use_tempaddr = 2
# Enable IPv6 stable privacy mode
net.ipv6.conf.wlp3s0.stable_secret = 25a3:36e6:9384:1111:9866:def1:abcd:1234
net.ipv6.conf.wlp3s0.addr_gen_mode = 2
```
Install additional packages for wifi and enable daemon on your wifi interface:
```
$ yay -S wpa_supplicant
# systemdctl enable wpa_supplicant@wlp3s0.service
```
Setup for mobile broadband follows later.
```
% systemctl enable systemd-networkd
% systemctl enable systemd-resolved
% rm /etc/resolv.conf
% ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
```
__The last command will break your DNS but that's fine because we will reboot in a moment.__
__Maybe you have to do this after reboot. Definitly check if it worked.__
## Set hostname
```
% echo "myhostname" > /etc/hostname
```
## Setup network time and timezone
```
% nvim /etc/systemd/timesyncd.conf
[Time]
NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org 2.arch.pool.ntp.org 3.arch.pool.ntp.org
FallbackNTP=0.de.pool.ntp.org 1.de.pool.ntp.org 2.de.pool.ntp.org 3.de.pool.ntp.org
% systemctl enable systemd-timesyncd
```
```
% ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime
% hwclock --systohc
```
## Locale setup
Edit `/etc/locale.gen` file in your chroot and uncomment `en_US.UTF-8 UTF-8` locales. Then run:
```
% locale-gen
```
```
% nvim /etc/locale.conf
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_COLLATE=C
```
## Set root password
```
% passwd
```
## Reboot into new system
```
% exit # exit chroot
# umount -R /mnt
# swapoff -a
# cryptsetup close cryptroot
# cryptsetup close cryptswap
# reboot
```
## Check if time is correct
```
# timedatectl status
Local time: Thu 2021-08-05 18:08:59 CEST
Universal time: Thu 2021-08-05 16:08:59 UTC
RTC time: Thu 2021-08-05 16:09:09
Time zone: Europe/Berlin (CEST, +0200)
System clock synchronized: no
NTP service: inactive
RTC in local TZ: no
```
Enable synchronisation with:
```
# timedatectl set-ntp true
```
## Create unprivileged user
```
# pacman -Syu sudo fish python pkgfile
# useradd -m -G wheel -s /usr/bin/fish bob
# EDITOR=nvim visudo
[...]
%wheel ALL=(ALL) ALL
[...]
# passwd bob
```
## Setup AUR
__Use unprivileged user from here on__
```
$ sudo pacman -S git
$ git clone https://aur.archlinux.org/yay.git
$ cd yay
$ makepkg -csi
$ cd ..
$ rm -fr yay
```
## Some quality of live things
### Update systemd-boot on ESP when systemd is updated
```
$ yay -S systemd-boot-pacman-hook
```
### System monitoring with temperatures
```
$ yay -S htop lsof strace lm_sensors
# sensors-detect # hit enter until it stops
```
### Use `dash` for `/bin/sh`
This could cause problems, use `checkbashisms` to check your scripts:
```
$ yay -S checkbashisms
$ find /usr/bin/ -type f -perm -o=r -print0 | xargs -0 gawk '/^#!.*( |[/])sh/{printf "%s\0", FILENAME} {nextfile}' | xargs -0 checkbashisms
$ yay -S dashbinsh
```
### Make pacman nice and fast
Edit `/etc/pacman.conf` and add/change these lines:
```
Color
VerbosePkgLists
ParallelDownloads = 10
ILoveCandy
```
Also enable multilib:
```
[multilib]
Include = /etc/pacman.d/mirrorlist
```
# Grafical setup
## Setup gdm
```
$ yay -S gdm
# systemctl enable gdm.service
```
## Setup XFCE with i3 as window manager
Install packages first. Choose as many from these groups as you like. You can probably skip most panel plugins:
```
$ yay -S xfce4 xfce4-goodies
```
My favorites even under i3 are: `xfce4-screenshooter`, `xfce4-terminal`, `thunar`
For `thunar` setup `gvfs`:
```
$ yay -S gvfs gvfs-afc gvfs-goa gvfs-google gvfs-gphoto2 gvfs-mtp gvfs-nfs gvfs-smb
```
Install i3 (or i3-gaps if you wanna waste precious space just for looks):
```
$ yay -S i3 i3blocks j4-dmenu-desktop
```
Switch to i3 as window manager:
```
xfconf-query -c xfce4-session -p /sessions/Failsafe/Client0_Command -t string -sa xfsettingsd
xfconf-query -c xfce4-session -p /sessions/Failsafe/Client1_Command -t string -sa i3
```
This should change the value from `xfwm4` to `i3` in `~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-session.xml`.
## Fonts
```
% yay -S ttf-droid ttf-dejavu \ # default stuff
noto-fonts noto-fonts-cjk noto-fonts-emoji noto-fonts-extra \ # emoji
adobe-source-code-pro-fonts \ # monospace
adobe-source-sans-fonts \ # sans-serif
adobe-source-han-sans-cn-fonts adobe-source-han-sans-tw-fonts adobe-source-han-serif-cn-fonts adobe-source-han-serif-tw-fonts \ # chinese
adobe-source-han-sans-jp-fonts adobe-source-han-serif-jp-fonts \ # japanese
adobe-source-han-sans-kr-fonts # korean
```
# Bonus!
## Optimize pacman/yay
```
$ yay -S ccache
```
In `/etc/makepkg.conf`:
- For `CFLAGS` change `-march=native` and `-mtune=native`.
- Set `MAKEFLAGS="-j8"` or any othe rnumber of cores you want to use
- For `BUILDENV` enable `ccache`
## Wayland
Enable wayland support for Qt5/6
```
$ yay -S qt5-wayland qt6-wayland glfw-wayland
```
Enable wayland support for electron apps by editing `~/.config/electron-flags.conf`:
```
--enable-features=UseOzonePlatform
--ozone-platform=wayland
```
Install XWayland to support X11 applications:
```
$ yay -S xorg-xwayland
```
## Sway
TBC
```
yay -S sway \
xdg-desktop-portal xdg-desktop-portal-wlr \ # screen sharing/webrtc
```
## Gnome
Simply install the gnome group which includes most gnome packages you'll need:
```
$ yay -S gnome gnome-tweaks webp-pixbuf-loader
```
After a few hundred lines of things scroll by this the stuff that was too fast to read:
Install these packages that add features you propably want:
```
$ yay -S qt5-svg \ # svg icon set support
vulkan-mesa-layers \ # vulkan support
vulkan-intel \ # vulkan support (intel)
gst-libav gst-plugins-ugly \ # media file support
intel-media-driver libva-intel-driver \ # media file support (intel)
eog-plugins \ # image support for eye of gnome
poppler-data \ # pdf feature support
p7zip unrar \ # file-roller archive support
gedit-plugins \
fwupd dmidecode \ # firmware update support
pipewire-alsa pipewire-pulse pipewire-zeroconf \ # pulseaudio replacement
xdg-desktop-portal xdg-desktop-portal-gtk \ # screen sharing/webrtc
gtk-engines \ # gtk2 themes
chromium \ # browser
evolution evolution-ews highlight \ # email client
```
Enable pipewire support in chromium by enabling: `chrome://flags/#enable-webrtc-pipewire-capturer`

View file

@ -0,0 +1,36 @@
title: Pokéshell
date: 2016-01-01 23:30
category: linux
tags: pokemon, shell, bash, fish, imagemagick
slug: pokeshell
A friend of me had the idea of a shell displaying a random pokémon on launch. I liked the idea and couldn't resist to implement it over christmas.
<!-- PELICAN_END_SUMMARY -->
What the package does is, it downloads all pokémon images in a defined range which is 1-151 by default (I'm old these are the ones I know) and converts the images to escape sequences. These are saved in `/usr/share/pokeshell`.
Now when you ipopen a new shell you could display one of them randomly by putting this code in your `~/.bashrc`:
```sh
cat /usr/share/pokeshell/\$((\$RANDOM % 151 + 1)).pokemon
```
Or if you use fish like me put this in your `~/.config/fish/functions/fish_prompt.fish`:
```sh
function fish_greeting
set r (random)
cat /usr/share/pokeshell/(math \"\$r % 151 + 1\").pokemon
end
```
The images are downloaded using the [Pokéapi][pokeapi]. For the conversation I use Imagemagick and a tool called [img2xterm][img2xterm].
You can find the package in the [AUR][aur-pokeshell].
[pokeapi]: http://pokeapi.co
[img2xterm]: https://github.com/rossy/img2xterm
[aur-pokeshell]: https://aur.archlinux.org/packages/pokeshell/

1
flex Submodule

@ -0,0 +1 @@
Subproject commit 7e99f9bbe681a5b7edef9748bc6f6ae10f4862a7

140
pelicanconf.py Normal file
View file

@ -0,0 +1,140 @@
from datetime import datetime
AUTHOR = 'XenGi'
SITEURL = ''
SITENAME = 'XenGis blog'
SITETITLE = 'XenGi'
SITESUBTITLE = ''
SITEDESCRIPTION = 'A typical way to world domination'
SITELOGO = SITEURL + '/images/avatar.png'
FAVICON = SITEURL + '/images/favicon.ico'
BROWSER_COLOR = '#333'
ROBOTS = 'index, follow'
CC_LICENSE = {
'name': 'Creative Commons Attribution-ShareAlike',
'version': '4.0',
'slug': 'by-sa',
'icon': True,
'language': 'en_US',
}
COPYRIGHT_NAME = AUTHOR
COPYRIGHT_YEAR = datetime.now().year
STATIC_PATHS = [
'extra/custom.css'
]
EXTRA_PATH_METADATA = {
'extra/custom.css': {'path': 'static/custom.css'},
}
CUSTOM_CSS = 'static/custom.css'
MAIN_MENU = True
PLUGINS = [
'post_stats',
]
PLUGIN_PATHS = [
'plugins/'
]
JINJA_ENVIRONMENT = {}
I18N_TEMPLATES_LANG = 'en'
DEFAULT_LANG = 'en'
OG_LOCALE = 'en_US'
LOCALE = 'en_US'
THEME = 'flex'
OUTPUT_PATH = 'output/'
PATH = 'content/'
TIMEZONE = 'Europe/Berlin'
# Feed generation is usually not desired when developing
FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None
AUTHOR_FEED_ATOM = None
AUTHOR_FEED_RSS = None
USE_FOLDER_AS_CATEGORY = False
# Social widget
MENUITEMS = []
SOCIAL = [
('Email', 'mailto:blog@xengi.de'),
]
MENUITEMS = [
('Archives', '/archives.html'),
('Categories', '/categories.html'),
('Tags', '/tags.html'),
]
STATIC_PATHS = [
'images',
]
DEFAULT_PAGINATION = False
USE_GOOGLE_FONTS = True
# Uncomment following line if you want document-relative URLs when developing
#RELATIVE_URLS = True
DEFAULT_CATEGORY = 'misc'
DISPLAY_PAGES_ON_MENU = True
DISPLAY_CATEGORIES_ON_MENU = True
MARKDOWN = {
'extension_configs': {
'markdown.extensions.codehilite': {'css_class': 'highlight'},
'markdown.extensions.extra': {},
'markdown.extensions.meta': {},
'markdown.extensions.nl2br': {},
'markdown.extensions.smarty': {},
'markdown.extensions.sane_lists': {},
},
'output_format': 'xhtml5',
}
PAGE_PATHS = ['pages']
STATIC_PATHS = ['images']
TYPOGRIFY = True
ARTICLE_URL = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/'
ARTICLE_SAVE_AS = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/index.html'
PAGE_URL = 'pages/{slug}/'
PAGE_SAVE_AS = 'pages/{slug}/index.html'
AUTHOR_URL = 'author/{slug}.html'
AUTHOR_SAVE_AS = 'author/{slug}.html'
CATEGORY_URL = 'category/{slug}.html'
CATEGORY_SAVE_AS = 'category/{slug}.html'
TAG_URL = 'tag/{slug}.html'
TAG_SAVE_AS = 'tag/{slug}.html'
# Flex settings
USE_LESS = True
## dark mode
THEME_COLOR = 'dark'
THEME_COLOR_AUTO_DETECT_BROWSER_PREFERENCE = True
THEME_COLOR_ENABLE_USER_OVERRIDE = True
## code highlight
PYGMENTS_STYLE = 'emacs'
PYGMENTS_STYLE_DARK = 'monokai'
#PYGMENTS_STYLE = 'solarized-light'
#PYGMENTS_STYLE_DARK = 'solarized-dark'
# Plugin settings
## related posts
RELATED_POSTS_MAX = 10
## sitemap
SITEMAP = {
'format': 'xml',
'priorities': {
'articles': 0.5,
'indexes': 0.5,
'pages': 0.5
},
'changefreqs': {
'articles': 'monthly',
'indexes': 'daily',
'pages': 'monthly'
}
}
## isso
ISSO_URL = ''

View file

@ -0,0 +1 @@
from .post_stats import *

View file

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
"""
Post Statistics
========================
This plugin calculates various statistics about a post and stores them in an article.stats dictionary:
wc: how many words
read_mins: how many minutes to read this article, based on 250 wpm (http://en.wikipedia.org/wiki/Words_per_minute#Reading_and_comprehension)
word_counts: frquency count of all the words in the article; can be used for tag/word clouds/
fi: Flesch-kincaid Index/ Reading Ease
fk: Flesch-kincaid Grade Level
"""
from pelican import signals
from bs4 import BeautifulSoup
import re
from collections import Counter
from .readability import *
def calculate_stats(instance):
if instance._content is not None:
stats = {}
content = instance._content
# How fast do average people read?
WPM = 250
# Use BeautifulSoup to get readable/visible text
raw_text = BeautifulSoup(content, 'html.parser').getText()
# Process the text to remove entities
entities = r'\&\#?.+?;'
raw_text = raw_text.replace('&nbsp;', ' ')
raw_text = re.sub(entities, '', raw_text)
# Flesch-kincaid readbility stats counts sentances,
# so save before removing punctuation
tmp = raw_text
# Process the text to remove punctuation
drop = u'.,?!@#$%^&*()_+-=\|/[]{}`~:;\'\"‘’—…“”'
raw_text = raw_text.translate(dict((ord(c), u'') for c in drop))
# Count the words in the text
words = raw_text.lower().split()
word_count = Counter(words)
# Return the stats
stats['word_counts'] = word_count
stats['wc'] = sum(word_count.values())
# Calulate how long it'll take to read, rounding up
stats['read_mins'] = (stats['wc'] + WPM - 1) // WPM
if stats['read_mins'] == 0:
stats['read_mins'] = 1
# Calculate Flesch-kincaid readbility stats
readability_stats = stcs, words, sbls = text_stats(tmp, stats['wc'])
stats['fi'] = "{:.2f}".format(flesch_index(readability_stats))
stats['fk'] = "{:.2f}".format(flesch_kincaid_level(readability_stats))
instance.stats = stats
def register():
signals.content_object_init.connect(calculate_stats)

View file

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
# Adadpted from here: http://acdx.net/calculating-the-flesch-kincaid-level-in-python/
# See here for details: http://en.wikipedia.org/wiki/Flesch%E2%80%93Kincaid_readability_test
from __future__ import division
import re
def mean(seq):
return sum(seq) / len(seq)
def syllables(word):
if len(word) <= 3:
return 1
word = re.sub(r"(es|ed|(?<!l)e)$", "", word)
return len(re.findall(r"[aeiouy]+", word))
def normalize(text):
terminators = ".!?:;"
term = re.escape(terminators)
text = re.sub(r"[^%s\sA-Za-z]+" % term, "", text)
text = re.sub(r"\s*([%s]+\s*)+" % term, ". ", text)
return re.sub(r"\s+", " ", text)
def text_stats(text, wc):
text = normalize(text)
stcs = [s.split(" ") for s in text.split(". ")]
stcs = [s for s in stcs if len(s) >= 2]
if wc:
words = wc
else:
words = sum(len(s) for s in stcs)
sbls = sum(syllables(w) for s in stcs for w in s)
return len(stcs), words, sbls
def flesch_index(stats):
stcs, words, sbls = stats
if stcs == 0 or words == 0:
return 0
return 206.835 - 1.015 * (words / stcs) - 84.6 * (sbls / words)
def flesch_kincaid_level(stats):
stcs, words, sbls = stats
if stcs == 0 or words == 0:
return 0
return 0.39 * (words / stcs) + 11.8 * (sbls / words) - 15.59

View file

@ -0,0 +1,49 @@
Post Statistics
==================
A Pelican plugin to calculate various statistics about a post and store them in an article.stats dictionary:
- ``wc``: how many words
- ``read_mins``: how many minutes would it take to read this article, based on 250 wpm (http://en.wikipedia.org/wiki/Words_per_minute#Reading_and_comprehension)
- ``word_counts``: frquency count of all the words in the article; can be used for tag/word clouds
- ``fi``: Flesch-kincaid Index/ Reading Ease (see: http://en.wikipedia.org/wiki/Flesch%E2%80%93Kincaid_readability_tests)
- ``fk``: Flesch-kincaid Grade Level
Example:
.. code-block:: python
{
'wc': 2760,
'fi': '65.94',
'fk': '7.65',
'word_counts': Counter({u'to': 98, u'a': 90, u'the': 83, u'of': 50, ...}),
'read_mins': 12
}
This allows you to output these values in your templates, like this, for example:
.. code-block:: html+jinja
<p title="~{{ article.stats['wc'] }} words">~{{ article.stats['read_mins'] }} min read</p>
<ul>
<li>Flesch-kincaid Index/ Reading Ease: {{ article.stats['fi'] }}</li>
<li>Flesch-kincaid Grade Level: {{ article.stats['fk'] }}</li>
</ul>
The ``word_counts`` variable is a python ``Counter`` dictionary and looks something like this, with each unique word and it's frequency:
.. code-block:: python
Counter({u'to': 98, u'a': 90, u'the': 83, u'of': 50, u'karma': 50, .....
and can be used to create a tag/word cloud for a post.
Requirements
----------------
`post_stats` requires BeautifulSoup.
.. code-block:: console
$ pip install beautifulsoup4

28
publishconf.py Normal file
View file

@ -0,0 +1,28 @@
#!/usr/bin/env python3
# This file is only used if you use `make publish` or
# explicitly specify it as your config file.
import os
import sys
sys.path.append(os.curdir)
from pelicanconf import *
# If your site is available via HTTPS, make sure SITEURL begins with https://
SITEURL = 'https://xengi.de'
RELATIVE_URLS = False
FEED_ATOM = 'feeds/atom.xml'
FEED_RSS = 'feeds/rss.xml'
FEED_ALL_ATOM = 'feeds/all.atom.xml'
FEED_ALL_RSS = 'feeds/all.rss.xml'
CATEGORY_FEED_ATOM = 'feeds/category/%s.atom.xml'
CATEGORY_FEED_RSS = 'feeds/category/%s.rss.xml'
AUTHOR_FEED_ATOM = 'feeds/author/%s.atom.xml'
AUTHOR_FEED_RSS = 'feeds/author/%s.rss.xml'
TAG_FEED_ATOM = 'feeds/tag/%s.atom.xml'
TAG_FEED_RSS = 'feeds/tag/%s.rss.xml'
DELETE_OUTPUT_DIRECTORY = True
USE_LESS = False

7
requirements.txt Normal file
View file

@ -0,0 +1,7 @@
pelican[markdown]
markdown
pelican-sitemap
pelican-related-posts
pelican-neighbors
typogrify
beautifulsoup4 # plugin: post_stats, summary

126
tasks.py Normal file
View file

@ -0,0 +1,126 @@
# -*- coding: utf-8 -*-
import os
import shlex
import shutil
import sys
import datetime
from invoke import task
from invoke.main import program
from invoke.util import cd
from pelican import main as pelican_main
from pelican.server import ComplexHTTPRequestHandler, RootedHTTPServer
from pelican.settings import DEFAULT_CONFIG, get_settings_from_file
SETTINGS_FILE_BASE = 'pelicanconf.py'
SETTINGS = {}
SETTINGS.update(DEFAULT_CONFIG)
LOCAL_SETTINGS = get_settings_from_file(SETTINGS_FILE_BASE)
SETTINGS.update(LOCAL_SETTINGS)
CONFIG = {
'settings_base': SETTINGS_FILE_BASE,
'settings_publish': 'publishconf.py',
# Output path. Can be absolute or relative to tasks.py. Default: 'output'
'deploy_path': SETTINGS['OUTPUT_PATH'],
# Host and port for `serve`
'host': 'localhost',
'port': 8000,
}
@task
def clean(c):
"""Remove generated files"""
if os.path.isdir(CONFIG['deploy_path']):
shutil.rmtree(CONFIG['deploy_path'])
os.makedirs(CONFIG['deploy_path'])
@task
def build(c):
"""Build local version of site"""
pelican_run('-s {settings_base}'.format(**CONFIG))
@task
def rebuild(c):
"""`build` with the delete switch"""
pelican_run('-d -s {settings_base}'.format(**CONFIG))
@task
def regenerate(c):
"""Automatically regenerate site upon file modification"""
pelican_run('-r -s {settings_base}'.format(**CONFIG))
@task
def serve(c):
"""Serve site at http://$HOST:$PORT/ (default is localhost:8000)"""
class AddressReuseTCPServer(RootedHTTPServer):
allow_reuse_address = True
server = AddressReuseTCPServer(
CONFIG['deploy_path'],
(CONFIG['host'], CONFIG['port']),
ComplexHTTPRequestHandler)
sys.stderr.write('Serving at {host}:{port} ...\n'.format(**CONFIG))
server.serve_forever()
@task
def reserve(c):
"""`build`, then `serve`"""
build(c)
serve(c)
@task
def preview(c):
"""Build production version of site"""
pelican_run('-s {settings_publish}'.format(**CONFIG))
@task
def livereload(c):
"""Automatically reload browser tab upon file modification."""
from livereload import Server
def cached_build():
cmd = '-s {settings_base} -e CACHE_CONTENT=True LOAD_CONTENT_CACHE=True'
pelican_run(cmd.format(**CONFIG))
cached_build()
server = Server()
theme_path = SETTINGS['THEME']
watched_globs = [
CONFIG['settings_base'],
'{}/templates/**/*.html'.format(theme_path),
]
content_file_extensions = ['.md', '.rst']
for extension in content_file_extensions:
content_glob = '{0}/**/*{1}'.format(SETTINGS['PATH'], extension)
watched_globs.append(content_glob)
static_file_extensions = ['.css', '.js']
for extension in static_file_extensions:
static_file_glob = '{0}/static/**/*{1}'.format(theme_path, extension)
watched_globs.append(static_file_glob)
for glob in watched_globs:
server.watch(glob, cached_build)
server.serve(host=CONFIG['host'], port=CONFIG['port'], root=CONFIG['deploy_path'])
@task
def publish(c):
"""Publish to production via rsync"""
pelican_run('-s {settings_publish}'.format(**CONFIG))
c.run(
'rsync --delete --exclude ".DS_Store" -pthrvz -c '
'-e "ssh -p {ssh_port}" '
'{} {ssh_user}@{ssh_host}:{ssh_path}'.format(
CONFIG['deploy_path'].rstrip('/') + '/',
**CONFIG))
def pelican_run(cmd):
cmd += ' ' + program.core.remainder # allows to pass-through args to pelican
pelican_main(shlex.split(cmd))