Compare commits
1 Commits
master
...
school-fal
Author | SHA1 | Date | |
---|---|---|---|
eb91b6dc88 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,5 +1,2 @@
|
||||
__pycache__/
|
||||
/output/
|
||||
/dat-version/
|
||||
/ipfs-version/
|
||||
/gergelypolonkaieu_site.egg-info/
|
||||
/Gemfile.lock
|
||||
_site/
|
||||
|
2
.hyde.el
Normal file
2
.hyde.el
Normal file
@ -0,0 +1,2 @@
|
||||
(setq hyde/git/remote "origin"
|
||||
hyde/git/remote-branch "master")
|
11
404.md
Normal file
11
404.md
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
layout: page
|
||||
title: Not Found
|
||||
permalink: /404.html
|
||||
---
|
||||
|
||||
The page you are looking for is not here. Maybe it was but I have removed it. Most likely it was intentionally. If you think I made a mistake, please tell me.
|
||||
|
||||
{% if page.url contains '/akarmi' %}
|
||||
If you are looking for the pictures that used to be here, you should definitely contact me. For reasons.
|
||||
{% endif %}
|
74
Makefile
74
Makefile
@ -1,74 +0,0 @@
|
||||
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
|
||||
|
||||
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 ssh_upload upload the web site via SSH '
|
||||
@echo ' make rsync_upload upload the web site via rsync+ssh '
|
||||
@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:
|
||||
ifdef PORT
|
||||
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT)
|
||||
else
|
||||
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
|
||||
endif
|
||||
|
||||
serve-global:
|
||||
ifdef SERVER
|
||||
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b $(SERVER)
|
||||
else
|
||||
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b 0.0.0.0
|
||||
endif
|
||||
|
||||
|
||||
devserver:
|
||||
ifdef PORT
|
||||
$(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT)
|
||||
else
|
||||
$(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
|
||||
endif
|
||||
|
||||
publish:
|
||||
$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(PUBLISHCONF) $(PELICANOPTS)
|
||||
|
||||
|
||||
.PHONY: html help clean regenerate serve serve-global devserver publish
|
20
README.md
20
README.md
@ -1,18 +1,2 @@
|
||||
# Website of Gergely Polonkai
|
||||
|
||||
## Publishing to the web
|
||||
|
||||
```
|
||||
poetry run pelican content
|
||||
rsync --archive --verbose --human-readable --delete output/ /data/gergely.polonkai.eu/html
|
||||
restorecon -vr /data/gergely.polonkai.eu/html
|
||||
```
|
||||
|
||||
## Publishing to IPFS
|
||||
|
||||
```
|
||||
poetry run pelican -s ipfspublish.py -o ipfs-version content
|
||||
cd ipfs-version
|
||||
ipfs add -r .
|
||||
ipfs name publish --key=gergely.polonkai.eu /ipfs/XXX
|
||||
```
|
||||
gergelypolonkai.github.io
|
||||
=========================
|
||||
|
19
_config.yml
Normal file
19
_config.yml
Normal file
@ -0,0 +1,19 @@
|
||||
# Site settings
|
||||
title: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
description: "developer, systems engineer and administrator"
|
||||
baseurl: ""
|
||||
url: "http://gergely.polonkai.eu"
|
||||
timezone: Europe/Budapest
|
||||
name: Gergely Polonkai
|
||||
paginate: 10
|
||||
paginate_path: "/blog/page/:num"
|
||||
exclude: ['README.md', 'Gemfile', 'Gemfile.lock', 'CNAME', ".hyde.el"]
|
||||
include: ['.well-known']
|
||||
plugins:
|
||||
- jekyll-gist
|
||||
- jekyll-paginate
|
||||
|
||||
# Build settings
|
||||
markdown: kramdown
|
||||
permalink: pretty
|
42
_data/contacts.yaml
Normal file
42
_data/contacts.yaml
Normal file
@ -0,0 +1,42 @@
|
||||
- text: E-mail
|
||||
link: mailto:gergely@polonkai.eu
|
||||
image: email.png
|
||||
- text: Stack Exchange
|
||||
link: http://stackexchange.com/users/1369500/gergelypolonkai
|
||||
image: stackexchange.png
|
||||
- text: LinkedIn
|
||||
link: http://www.linkedin.com/in/gergelypolonkai
|
||||
image: linkedin.png
|
||||
- text: Skype
|
||||
link: skype:gergely.polonkai
|
||||
image: skype.png
|
||||
- text: Facebook
|
||||
link: http://facebook.com/Polesz
|
||||
image: facebook.png
|
||||
- text: Google+
|
||||
link: https://plus.google.com/+GergelyPolonkai/about
|
||||
image: google_plus.png
|
||||
- text: Twitter
|
||||
link: http://twitter.com/GergelyPolonkai
|
||||
image: twitter.png
|
||||
- text: Tumblr
|
||||
link: http://gergelypolonkai.tumblr.com
|
||||
image: tumblr.png
|
||||
- text: deviantArt
|
||||
link: http://gergelypolonkai.deviantart.com
|
||||
image: deviantart.png
|
||||
- text: Hashnode
|
||||
link: https://hashnode.com/@gergelypolonkai
|
||||
image: hashnode.png
|
||||
- text: Keybase
|
||||
link: https://keybase.io/gergelypolonkai
|
||||
image: keybase.png
|
||||
- text: Liberapay
|
||||
link: https://liberapay.com/gergelypolonkai
|
||||
image: liberapay.png
|
||||
- text: Mastodon
|
||||
link: https://social.polonkai.eu/@gergely
|
||||
image: mastodon.png
|
||||
- text: Pay me a coffee
|
||||
link: https://paypal.me/GergelyPolonkai/250
|
||||
image: paypal.png
|
1738
_data/symbolon.json
Normal file
1738
_data/symbolon.json
Normal file
File diff suppressed because it is too large
Load Diff
15
_drafts/gtk-actionable-in-action.md
Normal file
15
_drafts/gtk-actionable-in-action.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
layout: post
|
||||
title: "GtkActionable in action"
|
||||
author:
|
||||
name: "Gergely Polonkai"
|
||||
email: "gergely@polonkai.eu"
|
||||
---
|
||||
|
||||
I have seen several people (including myself) struggling with
|
||||
disabling/enabling menu items, toolbar buttons and similar UI
|
||||
interfaces based on different conditions. It gets even worse if there
|
||||
are multiple representations of the same action in the same
|
||||
application, e.g. a menu item and a toolbar button exists for the same
|
||||
action. But with GTK+ 3.4, we have GtkAction, which is exactly for
|
||||
this kind of situations.
|
@ -0,0 +1,17 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Measuring code coverage with codecov for libtool projects"
|
||||
author:
|
||||
name: "Gergely Polonkai"
|
||||
email: "gergely@polonkai.eu"
|
||||
---
|
||||
|
||||
I have recently found [codecov][https://codecov.io/]; they offer free
|
||||
services for public GitHub projects. As I have recently started writing
|
||||
tests for my SWE-GLib project, I decided to give it a go. Things are not
|
||||
this easy if you use GNU Autotools and libtool, though…
|
||||
|
||||
The problem here is that these tools generate output under `src/.libs/`
|
||||
(given that your sources are under `src/`) and `gcov` has hard times
|
||||
finding the coverage data files. Well, at least in the codecov
|
||||
environment, it works fine on my machine.
|
326
_drafts/writing-a-gnome-shell-extension.md
Normal file
326
_drafts/writing-a-gnome-shell-extension.md
Normal file
@ -0,0 +1,326 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Writing a GNOME Shell extension"
|
||||
---
|
||||
|
||||
I could not find a good tutorial on how to write a GNOME Shell
|
||||
extension. There is a so called step by step
|
||||
[instruction list](https://wiki.gnome.org/Projects/GnomeShell/Extensions/StepByStepTutorial)
|
||||
on how to do it, but it has its flaws, including grammar and clearance.
|
||||
As I wanted to create an extension for my SWE GLib library to display
|
||||
the current position of some planets, I dug into existing (and working)
|
||||
extensions’ source code and made up something. Comments welcome!
|
||||
|
||||
---
|
||||
|
||||
GNOME Shell extensions are written in JavaScript and are interpreted
|
||||
by [GJS](https://wiki.gnome.org/action/show/Projects/Gjs). Using
|
||||
introspected libraries from JavaScript is not a problem for me (see
|
||||
SWE GLib’s
|
||||
[Javascript example](https://github.com/gergelypolonkai/swe-glib/blob/master/examples/basic.js);
|
||||
it’s not beautiful, but it’s working), but wrapping your head around
|
||||
the Shell’s concept can take some time.
|
||||
|
||||
The Shell is a Clutter stage, and all the buttons (including the
|
||||
top-right “Activities” button) are actors on this stage. You can add
|
||||
practically anything to the Shell panel that you can add to a Clutter
|
||||
stage.
|
||||
|
||||
The other thing to remember is the lifecycle of a Shell
|
||||
extension. After calling `init()`, there are two ways forward: you
|
||||
either use a so called extension controller, or plain old JavaScript
|
||||
functions `enable()` and `disable()`; I will go on with the former
|
||||
method for reasons discussed later.
|
||||
|
||||
If you are fine with the `enable()`/`disable()` function version, you
|
||||
can ease your job with the following command:
|
||||
|
||||
```
|
||||
gnome-shell-extension-tool --create-extension
|
||||
```
|
||||
|
||||
This will ask you a few parameters and create the necessary files for
|
||||
you. On what these parameters should look like, please come with me to
|
||||
the next section.
|
||||
|
||||
## Placement and naming
|
||||
|
||||
Extensions reside under `$HOME/.local/share/gnome-shell/extensions`,
|
||||
where each of them have its own directory. The directory name has to be
|
||||
unique, of course; to achieve this, they are usually the same as the
|
||||
UUID of the extension.
|
||||
|
||||
The UUID is a string of alphanumeric characters, with some extras added.
|
||||
Generally, it should match this regular expression:
|
||||
`^[-a-zA-Z0-9@._]+$`. The convention is to use the form
|
||||
`extension-name@author-id`, e.g. `Planets@gergely.polonkai.eu`. Please
|
||||
see
|
||||
[this link](https://wiki.gnome.org/Projects/GnomeShell/Extensions/UUIDGuidelines)
|
||||
for some more information about this.
|
||||
|
||||
## Anatomy of an extension
|
||||
|
||||
Extensions consist of two main parts, `metadata.json` and
|
||||
`extension.js`.
|
||||
|
||||
The `metadata.json` file contains compatibility information and, well,
|
||||
some meta data:
|
||||
|
||||
```json
|
||||
{
|
||||
"shell-version": ["3.18"],
|
||||
"uuid": "planets@gergely.polonkai.eu",
|
||||
"name": "Planets",
|
||||
"description": "Display current planet positions"
|
||||
}
|
||||
```
|
||||
|
||||
Here, `shell-version` must contain all versions of GNOME Shell that is
|
||||
known to load and display your extension correctly. You can insert minor
|
||||
versions here, like I did, or exact version numbers, like `3.18.1`.
|
||||
|
||||
In the `extension.js` file, which contains the actual extension code,
|
||||
the only thing you actually need is an `init()` function:
|
||||
|
||||
```javascript
|
||||
function init(extensionMeta) {
|
||||
// Do whatever it takes to initialize your extension, like
|
||||
// initializing the translations. However, never do any widget
|
||||
// magic here yet.
|
||||
|
||||
// Then return the controller object
|
||||
return new ExtensionController(extensionMeta);
|
||||
}
|
||||
```
|
||||
|
||||
## Extension controller
|
||||
|
||||
So far so good, but what is this extension controller thing? It is an
|
||||
object which is capable of managing your GNOME Shell extension. Whenever
|
||||
the extension is loaded, its `enable()` method is called; when the
|
||||
extension is unloaded, you guessed it, the `disable()` method gets
|
||||
called.
|
||||
|
||||
```javascript
|
||||
function ExtensionController(extensionMeta) {
|
||||
return {
|
||||
extensionMeta: extensionMeta,
|
||||
extension: null,
|
||||
|
||||
enable: function() {
|
||||
this.extension = new PlanetsExtension(this.extensionMeta);
|
||||
|
||||
Main.panel.addToStatusArea("planets",
|
||||
this.extension,
|
||||
0, "right");
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this.extension.actor.destroy();
|
||||
this.extension.destroy();
|
||||
|
||||
this.extension = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This controller will create a new instance of the `PlanetsExtension`
|
||||
class and add it to the panel’s right side when loaded. Upon
|
||||
unloading, the extension’s actor gets destroyed (which, as you will
|
||||
see later, gets created behind the scenes, not directly by us),
|
||||
together with the extension itself. Also, for safety measures, the
|
||||
extension is set to `null`.
|
||||
|
||||
## The extension
|
||||
|
||||
The extension is a bit more tricky, as, for convenience reasons, it
|
||||
should extend an existing panel widget type.
|
||||
|
||||
```javascript
|
||||
function PlanetsExtension(extensionMeta) {
|
||||
this._init(extensionMeta);
|
||||
}
|
||||
|
||||
PlanetsExtension.prototype = {
|
||||
__proto__ = PanelMenu.Button.prototype,
|
||||
|
||||
_init: function(extensionMeta) {
|
||||
PanelMenu.Button.prototype._init.call(this, 0.0);
|
||||
|
||||
this.extensionMeta = extensionMeta;
|
||||
|
||||
this.panelContainer = new St.BoxLayout({style_class: 'panel-box'});
|
||||
this.actor.add_actor(this.panelContainer);
|
||||
this.actor.add_style_class_name('panel-status-button');
|
||||
|
||||
this.panelLabel = new St.Label({
|
||||
text: 'Loading',
|
||||
y_align: Clutter.ActorAlign.CENTER
|
||||
});
|
||||
|
||||
this.panelContainer.add(this.panelLabel);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Here we extend the Button class of panelMenu, so we will be able to do
|
||||
some action upon activate.
|
||||
|
||||
The only parameter passed to the parent’s `_init()` function is
|
||||
`menuAlignment`, with the value `0.0`, which is used to position the
|
||||
menu arrow. (_Note: I cannot find any documentation on this, but it
|
||||
seems that with the value `0.0`, a menu arrow is not added._)
|
||||
|
||||
The extension class in its current form is capable of creating the
|
||||
actual panel button displaying the text “Loading” in its center.
|
||||
|
||||
## Loading up the extension
|
||||
|
||||
Now with all the necessary import lines added:
|
||||
|
||||
```javascript
|
||||
// The PanelMenu module that contains Button
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
// The St class that contains lots of UI functions
|
||||
const St = imports.gi.St;
|
||||
// Clutter, which is used for displaying everything
|
||||
const Clutter = imports.gi.Clutter;
|
||||
```
|
||||
|
||||
As soon as this file is ready, you can restart your Shell (press
|
||||
Alt-F2 and enter the command `r`), and load the extension with
|
||||
e.g. the GNOME Tweak Tool. You will see the Planets button on the
|
||||
right. This little label showing the static text “Planets”, however,
|
||||
is pretty boring, so let’s add some action.
|
||||
|
||||
## Adding some periodical change
|
||||
|
||||
Since the planets’ position continuously change, we should update our
|
||||
widget every minute or so. Let’s patch our `_init()` a bit:
|
||||
|
||||
```javascript
|
||||
this.last_update = 0;
|
||||
|
||||
MainLoop.timeout_add(1, Lang.bind(this, function() {
|
||||
this.last_update++;
|
||||
this.panelLabel.set_text("Update_count: " + this.last_update);
|
||||
}))
|
||||
```
|
||||
|
||||
This, of course, needs a new import line for `MainLoop` to become available:
|
||||
|
||||
```javascript
|
||||
const MainLoop = imports.mainloop;
|
||||
const Lang = imports.lang;
|
||||
```
|
||||
|
||||
Now if you restart your Shell, your brand new extension will increase
|
||||
its counter every second. This, however, presents some problems.
|
||||
|
||||
SWE GLib queries can sometimes be expensive, both in CPU and disk
|
||||
operations, so updating our widget every second may present problems.
|
||||
Also, planets don’t go **that** fast. We may update our timeout value
|
||||
from `1` to `60` or something, but why don’t just give our user a chance
|
||||
to set it?
|
||||
|
||||
## Introducing settings
|
||||
|
||||
Getting settings from `GSettings` is barely straightforward, especially
|
||||
for software installed in a non-GNOME directory (which includes
|
||||
extensions). To make our lives easier, I copied over a
|
||||
[convenience library](https://github.com/projecthamster/shell-extension/blob/master/convenience.js)
|
||||
from the [Hamster project](https://projecthamster.wordpress.com/)’s
|
||||
extension, originally written by Giovanni Campagna. The relevant
|
||||
function here is `getSettings()`:
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* getSettings:
|
||||
* @schema: (optional): the GSettings schema id
|
||||
*
|
||||
* Builds and return a GSettings schema for @schema, using schema files
|
||||
* in extensionsdir/schemas. If @schema is not provided, it is taken from
|
||||
* metadata['settings-schema'].
|
||||
*/
|
||||
function getSettings(schema) {
|
||||
let extension = ExtensionUtils.getCurrentExtension();
|
||||
|
||||
schema = schema || extension.metadata['settings-schema'];
|
||||
|
||||
const GioSSS = Gio.SettingsSchemaSource;
|
||||
|
||||
// check if this extension was built with "make zip-file", and thus
|
||||
// has the schema files in a subfolder
|
||||
// otherwise assume that extension has been installed in the
|
||||
// same prefix as gnome-shell (and therefore schemas are available
|
||||
// in the standard folders)
|
||||
let schemaDir = extension.dir.get_child('schemas');
|
||||
let schemaSource;
|
||||
if (schemaDir.query_exists(null))
|
||||
schemaSource = GioSSS.new_from_directory(schemaDir.get_path(),
|
||||
GioSSS.get_default(),
|
||||
false);
|
||||
else
|
||||
schemaSource = GioSSS.get_default();
|
||||
|
||||
let schemaObj = schemaSource.lookup(schema, true);
|
||||
if (!schemaObj)
|
||||
throw new Error('Schema ' + schema + ' could not be found for extension '
|
||||
+ extension.metadata.uuid + '. Please check your installation.');
|
||||
|
||||
return new Gio.Settings({ settings_schema: schemaObj });
|
||||
}
|
||||
```
|
||||
|
||||
You can either incorporate this function into your `extension.js` file,
|
||||
or just use `convenience.js` file like I (and the Hamster applet) did
|
||||
and import it:
|
||||
|
||||
```javascript
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension;
|
||||
const Convenience = Me.imports.convenience;
|
||||
```
|
||||
|
||||
Now let’s create the settings definition. GSettings schema files are XML
|
||||
files. We want to add only one settings for now, the refresh interval.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<schemalist>
|
||||
<schema id="org.gnome.shell.extensions.planets" path="/org/gnome/shell/extensions/planets/">
|
||||
<key name="refresh-interval" type="i">
|
||||
<default>30</default>
|
||||
<summary>Refresh interval of planet data</summary>
|
||||
<description>Interval in seconds. Sets how often the planet positions are recalculated. Setting this too low (e.g. below 30) may raise performance issues.</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
```
|
||||
you need to compile these settings with
|
||||
|
||||
glib-compile-schemas --strict schemas/
|
||||
|
||||
Now let’s utilize this new setting. In the extension’s `_init()`
|
||||
function, add the following line:
|
||||
|
||||
```javascript
|
||||
this._settings = Convenience.getSettings();
|
||||
```
|
||||
|
||||
And, for `getSettings()` to work correctly, we also need to extend our
|
||||
`metadata.json` file:
|
||||
|
||||
```json
|
||||
"settings-schema": "planets"
|
||||
```
|
||||
|
||||
After another restart (please, GNOME guys, add an option to reload
|
||||
extensions!), your brand new widget will refresh every 30 seconds.
|
||||
|
||||
## Displaying the planet positions
|
||||
|
||||
## The settings panel
|
||||
|
||||
## Start an application
|
67
_drafts/writing-an-sdk.md
Normal file
67
_drafts/writing-an-sdk.md
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Lessens you learn while writing an SDK"
|
||||
date: 2016-03-19 12:34:56
|
||||
tags: [development]
|
||||
published: false
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
In the last few months I’ve been working on a GLib based SDK for
|
||||
client applications that want to communicate with a Matrix.org
|
||||
homeserver.
|
||||
|
||||
For whoever doesn’t know it, Matrix is a decentralized network of
|
||||
servers (Homeservers). Clients can connect to them via HTTP and send
|
||||
messages (events, in Matrix terminology) to each other. They are
|
||||
called events because these messages can be pretty much anything from
|
||||
instant messages through automated notifications to files or, well,
|
||||
actual events (such as a vCalendar); anything that you can serialize
|
||||
to JSON can go through this network.
|
||||
|
||||
My original intention was to integrate Matrix based chat into
|
||||
Telepathy, a DBus based messaging framework used by e.g. the GNOME
|
||||
desktop (more specifically Empathy, GNOME's chat client.) After
|
||||
announcing my plans among the Matrix devs, I quickly learned some
|
||||
things:
|
||||
|
||||
1. they are more than open to any development ideas
|
||||
1. they really wanted to see this working
|
||||
1. they would have been happy if there were a GLib or Qt based SDK
|
||||
|
||||
With my (far from complete) knowledge in GLib I decided to move on
|
||||
with this last point, hoping that it will help me much when I finally
|
||||
implement the Telepathy plugin.
|
||||
|
||||
## Matrix devs are open minded
|
||||
|
||||
What I learned very quickly is that Matrix devs are very open minded
|
||||
folks from different parts of the world. They are all individuals with
|
||||
their own ideas, experiences and quirks, yet, when it comes to that,
|
||||
they steer towards their goals as a community. Thus, getting
|
||||
additional information from them while reading the spec was super
|
||||
easy.
|
||||
|
||||
## The specification is easy to understand
|
||||
|
||||
Except when it is not. For these cases, see the previous point.
|
||||
|
||||
Jokes asidu, anyone who worked with communications protocols or JSON
|
||||
APIs before can get along with it fast. The endpoints are all
|
||||
documented, and if something is unclear, they are happy to help
|
||||
(especially if you patch up the spec afterwards.)
|
||||
|
||||
|
||||
## Copying the SDK for a different language is not (always) what you want
|
||||
|
||||
I started my SDK in C, trying to mimic the Python SDK. This was a
|
||||
double fail: the Python SDK was a volatile WiP, and C and Python are
|
||||
fundamentally different.
|
||||
|
||||
During the upcoming weeks this became clear and I switched to the Vala
|
||||
language. It is much easier to write GObject based stuff in Vala,
|
||||
although I had to fall back to C to get some features working. I also
|
||||
planned and implemented a more object oriented API, which is easier to
|
||||
use in the GObject world.
|
27
_includes/about.html
Normal file
27
_includes/about.html
Normal file
@ -0,0 +1,27 @@
|
||||
<p>
|
||||
Gergely Polonkai is a systems engineer of a telco company, and
|
||||
also a freelancer self- and software developer.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
He is learning about different IT subjects since the late
|
||||
1990s. These include web development, application building,
|
||||
systems engineering, IT security and many others. He also dug his
|
||||
nose deeply into free software, dealing with different types of
|
||||
Linux and its applications,
|
||||
while also writing and contributing to some open source projects.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
On this site he is writing posts about different stuff he faces
|
||||
during work (oh my, yet another IT solutions blog), hoping they
|
||||
can help others with their job, or just to get along with their
|
||||
brand new netbook that shipped with Linux.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
“I believe one can only achieve success if they follow their own
|
||||
instincts and listen to, but not bend under others’ opinions. If
|
||||
you change your course just because someone says so, you are
|
||||
following their instincts, not yours.”
|
||||
</p>
|
49
_includes/blog-post.html
Normal file
49
_includes/blog-post.html
Normal file
@ -0,0 +1,49 @@
|
||||
<article class="{% if page.post_listing %}col-sm-5 col-md-6 {% endif%}post">
|
||||
{% if page.post_listing %}
|
||||
<ul class="list-inline">
|
||||
<li class="col-md-8">
|
||||
{% endif %}
|
||||
<header class="post-header">
|
||||
{% if page.tag %}
|
||||
<h5>
|
||||
{% else %}
|
||||
<h3>
|
||||
{% endif %}
|
||||
{% if page.post_listing %}
|
||||
<a href="{{post.url | prepend: site.baseurl}}">
|
||||
{% endif %}
|
||||
{{post.title}}
|
||||
{% if page.post_listing %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if layout.render_post %}
|
||||
<div class="plusone-container"><div class="g-plusone" data-annotation="inline" data-size="small" data-width="300"></div></div>
|
||||
{% endif %}
|
||||
{% if page.tag %}
|
||||
</h5>
|
||||
{% else %}
|
||||
</h3>
|
||||
{% endif %}
|
||||
<div class="meta pull-left">
|
||||
{{post.author.name}}
|
||||
</div>
|
||||
<div class="meta pull-right">
|
||||
{{post.date | date: "%b %-d, %Y :: %H:%M"}}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{% if layout.render_post %}
|
||||
{{content}}
|
||||
{% else %}
|
||||
{{post.excerpt}}
|
||||
{% endif %}
|
||||
</main>
|
||||
|
||||
{% include tag-link.html %}
|
||||
{% if layout.post_listing %}
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</article>
|
14
_includes/disqus.html
Normal file
14
_includes/disqus.html
Normal file
@ -0,0 +1,14 @@
|
||||
<div id="disqus_thread"></div>
|
||||
<script type="text/javascript">
|
||||
var disqus_shortname = 'gergelypolonkai';
|
||||
|
||||
(function() {
|
||||
var dsq = document.createElement('script');
|
||||
dsq.type = 'text/javascript';
|
||||
dsq.async = true;
|
||||
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
|
||||
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||||
})();
|
||||
</script>
|
||||
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
|
||||
<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
|
16
_includes/head.html
Normal file
16
_includes/head.html
Normal file
@ -0,0 +1,16 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="keywords" content="{{page.keywords}}">
|
||||
<meta name="description" content="Personal page of Gergely Polonkai">
|
||||
<title>Gergely Polonkai{% if page.title %}: {{page.title}}{% endif %}</title>
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="{{'/favicon.ico' | prepend: site.baseurl}}">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,400italic,600,600italic,700,700italic,800,800italic" rel="stylesheet" type="text/css">
|
||||
<link rel="alternate" type="application/rss+xml" title="Gergely Polonkai's Blog - RSS Feed" href="{{site.url}}/blog/atom.xml">
|
||||
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="{{'/css/style.css' | prepend: site.baseurl}}">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/jquery.terminal/1.6.3/css/jquery.terminal.min.css" rel="stylesheet"/>
|
||||
|
||||
<script type="text/javascript" src="//code.jquery.com/jquery-2.1.3.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.terminal/1.6.3/js/jquery.terminal.min.js"></script>
|
35
_includes/header.html
Normal file
35
_includes/header.html
Normal file
@ -0,0 +1,35 @@
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#gp-navbar">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="{{'/' | prepend: site.baseurl}}"><img src="{{'/images/profile.svg' | prepend: site.baseurl}}" alt="Gergely Polonkai" style="background-color: white; height: 45px; margin-top: -13px;"></a>
|
||||
{% if page.url != '/' %}
|
||||
<a class="navbar-brand" href="{{'/' | prepend: site.baseurl}}">Gergely Polonkai</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="collapse navbar-collapse" id="gp-navbar">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="{{'/blog' | prepend: site.baseurl}}">Blog</a></li>
|
||||
<li><a href="{{'/resume' | prepend: site.baseurl}}">Resume</a></li>
|
||||
<li><a href="{{'/stories' | prepend: site.baseurl}}">Stories</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a href="https://about.me/gergely.polonkai">about.me</a></li>
|
||||
<li><a href="{{'/disclaimer' | prepend: site.baseurl}}">Disclaimer</a></li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="glyphicon glyphicon-pencil"></span> Contact me <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{% for contact in site.data.contacts %}
|
||||
<li><a href="{{contact.link}}" target="_blank"><img src="{{'/images/contact/' | prepend: site.baseurl | append: contact.image}}" alt="" /> {{contact.text}}</a></li>
|
||||
{% endfor %}
|
||||
<li><a href="{{'/blog/atom.xml' | prepend: site.baseurl}}"><img src="{{'/images/contact/feed.png' | prepend: site.baseurl}}" alt="" /> RSS Feed</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
17
_includes/pagination.html
Normal file
17
_includes/pagination.html
Normal file
@ -0,0 +1,17 @@
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
<li{% if paginator.previous_page == null %} class="disabled"{% endif %}>
|
||||
<a href="{{paginator.previous_page_path | prepend: site.baseurl | replace: '//', '/'}}" aria-label="Previous page">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% for page in (1..paginator.total_pages) %}
|
||||
<li{% if paginator.page == page %} class="active"{% endif %}><a href="{% if page == 1 %}{{'/blog' | prepend: site.baseurl}}{% else %}{{site.paginate_path | prepend: site.baseurl | replace: '//', '/' | replace: ':num', page}}{% endif %}">{{page}}</a></li>
|
||||
{% endfor %}
|
||||
<li{% if paginator.next_page == null %} class="disabled"{% endif %}>
|
||||
<a href="{{paginator.next_page_path | prepend: site.baseurl | replace: '//', '/'}}" aria-label="Next page">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
9
_includes/post-list.html
Normal file
9
_includes/post-list.html
Normal file
@ -0,0 +1,9 @@
|
||||
<div class="container-fluid">
|
||||
{% for post in posts limit: post_limit %}
|
||||
{% capture counter %}{% cycle 'odd', 'even' %}{% endcapture %}
|
||||
{% include blog-post.html %}
|
||||
{% if counter == 'even' %}
|
||||
<div class="clearfix"></div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
11
_includes/tag-link.html
Normal file
11
_includes/tag-link.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% capture tagsize %}{{post.tags | size}}{% endcapture %}
|
||||
{% if tagsize != '0' %}
|
||||
<footer>
|
||||
<p class="article-tags">
|
||||
{% for tag in post.tags %}
|
||||
<a href="{{tag | prepend: '/blog/tag/' | prepend: site.baseurl}}" class="tag-label">{{tag}}</a>
|
||||
{% endfor %}
|
||||
</p>
|
||||
<br class="clearfix">
|
||||
</footer>
|
||||
{% endif %}
|
130
_layouts/default.html
Normal file
130
_layouts/default.html
Normal file
@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
{% include head.html %}
|
||||
</head>
|
||||
<body>
|
||||
{% include header.html %}
|
||||
<div class="container" id="main-container">
|
||||
|
||||
{{content}}
|
||||
|
||||
{% if page.name != 'about.html' %}
|
||||
<div class="well well-sm small">
|
||||
<div class="pull-left" id="about-well-image">
|
||||
<a href="{{ site_url }}/about/">
|
||||
<img src="{{'/images/profile.svg' | prepend: site.baseurl}}" alt="">
|
||||
</a>
|
||||
</div>
|
||||
{% include about.html %}
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#tagcloud-button').click(function() {
|
||||
$('#tag-cloud').toggle('slow');
|
||||
});
|
||||
});
|
||||
|
||||
(function() {
|
||||
var po = document.createElement('script');
|
||||
po.type = 'text/javascript';
|
||||
po.async = true;
|
||||
po.src = 'https://apis.google.com/js/client:plusone.js?onload=start';
|
||||
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(po, s);
|
||||
})();
|
||||
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-43569023-1', 'polonkai.eu');
|
||||
ga('send', 'pageview');
|
||||
|
||||
jQuery.extend_if_has = function(desc, source, array) {
|
||||
for (var i=array.length;i--;) {
|
||||
if (typeof source[array[i]] != 'undefined') {
|
||||
desc[array[i]] = source[array[i]];
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
};
|
||||
|
||||
(function($) {
|
||||
$.fn.tilda = function(eval, options) {
|
||||
if ($('body').data('tilda')) {
|
||||
return $('body').data('tilda').terminal;
|
||||
}
|
||||
this.addClass('tilda');
|
||||
options = options || {};
|
||||
eval = eval || function(command, term) {
|
||||
term.echo("you don't set eval for tilda");
|
||||
};
|
||||
var settings = {
|
||||
prompt: 'guest@gergely.polonkai.eu> ',
|
||||
name: 'tilda',
|
||||
height: 400,
|
||||
enabled: false,
|
||||
greetings: 'Welcome to my Terminal. Type `help\' to list the available commands.\n\nPowered by http://terminal.jcubic.pl',
|
||||
keypress: function(e) {
|
||||
if (e.which == 96) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (options) {
|
||||
$.extend(settings, options);
|
||||
}
|
||||
this.append('<div class="td"></div>');
|
||||
var self = this;
|
||||
self.terminal = this.find('.td').terminal(eval, settings);
|
||||
var focus = false;
|
||||
$(document.documentElement).keypress(function(e) {
|
||||
console.log(e);
|
||||
if (e.which == 96) {
|
||||
self.slideToggle('fast');
|
||||
self.terminal.focus(focus = !focus);
|
||||
self.terminal.attr({
|
||||
scrollTop: self.terminal.attr("scrollHeight")
|
||||
});
|
||||
}
|
||||
});
|
||||
$('body').data('tilda', this);
|
||||
this.hide();
|
||||
return self;
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
String.prototype.strip = function(char) {
|
||||
return this.replace(new RegExp("^\\s*"), '')
|
||||
.replace(new RegExp("\\s*$"), '');
|
||||
}
|
||||
|
||||
jQuery(document).ready(function($) {
|
||||
$('#tilda').tilda(function(command, terminal) {
|
||||
command = command.strip();
|
||||
|
||||
switch (command) {
|
||||
case 'help':
|
||||
terminal.echo('about - Go to the about page');
|
||||
terminal.echo(' ');
|
||||
terminal.echo('More commands will follow soon!');
|
||||
|
||||
break;
|
||||
case 'about':
|
||||
location = '{{ site_url }}/about/';
|
||||
|
||||
break;
|
||||
default:
|
||||
terminal.echo(command + ': command not found');
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div id="tilda"></div>
|
||||
</body>
|
||||
</html>
|
15
_layouts/page.html
Normal file
15
_layouts/page.html
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
<div class="post">
|
||||
|
||||
<header class="post-header">
|
||||
<h2>{{page.title}}</h2>
|
||||
<div class="clearfix"></div>
|
||||
</header>
|
||||
|
||||
<article class="post-content">
|
||||
{{content}}
|
||||
</article>
|
||||
|
||||
</div>
|
20
_layouts/post.html
Normal file
20
_layouts/post.html
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
layout: default
|
||||
render_post: true
|
||||
---
|
||||
{% assign post = page %}
|
||||
{% include blog-post.html %}
|
||||
<div class="g-plus" data-action="share" data-height="15"></div>
|
||||
|
||||
<nav>
|
||||
<ul class="pager">
|
||||
{% if page.previous %}
|
||||
<li class="previous"><a href="{{page.previous.url | prepend: site.baseurl}}">← {{page.previous.title}}</a></li>
|
||||
{% endif %}
|
||||
{% if page.next %}
|
||||
<li class="next"><a href="{{page.next.url | prepend: site.baseurl}}">{{page.next.title}} →</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{% include disqus.html %}
|
15
_layouts/posts-by-tag.html
Normal file
15
_layouts/posts-by-tag.html
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
layout: default
|
||||
post_listing: true
|
||||
---
|
||||
<h3 class="tag">{{ page.tag }}</h3>
|
||||
{{content}}
|
||||
|
||||
<h4>Articles under this tag</h4>
|
||||
|
||||
{% if site.tags[page.tag] %}
|
||||
{% assign posts = site.tags[page.tag] %}
|
||||
{% include post-list.html %}
|
||||
{% else %}
|
||||
No posts with this tag.
|
||||
{% endif %}
|
43
_make_tags.sh
Normal file
43
_make_tags.sh
Normal file
@ -0,0 +1,43 @@
|
||||
#! /bin/sh
|
||||
#
|
||||
# Find all tags in all posts under _posts, and generate a file for
|
||||
# each under blog/tag. Also, if a tag page does not contain the tag:
|
||||
# or layout: keywords, the script will include them in the front
|
||||
# matter.
|
||||
|
||||
layout="posts-by-tag"
|
||||
|
||||
for tag in `grep -h ^tags: _posts/* | sed -re 's/^tags: +\[//' -e 's/\]$//' -e 's/, /\n/g' | sort | uniq`
|
||||
do
|
||||
tag_file="blog/tag/${tag}.md"
|
||||
echo -n "[$tag] "
|
||||
|
||||
if [ ! -f $tag_file ]
|
||||
then
|
||||
echo "creating ($tag_file)"
|
||||
|
||||
cat <<EOF > $tag_file
|
||||
---
|
||||
layout: $layout
|
||||
tag: $tag
|
||||
---
|
||||
EOF
|
||||
else
|
||||
updated=0
|
||||
if ! egrep "^tag: +${tag}$" $tag_file 2>&1 > /dev/null; then
|
||||
echo "adding tag"
|
||||
sed -i "0,/---/! s/---/tag: $tag\\n---/" $tag_file
|
||||
updated=1
|
||||
fi
|
||||
|
||||
if ! egrep "^layout: +" $tag_file 2>&1 > /dev/null; then
|
||||
echo "adding layout"
|
||||
sed -i "0,/---/! s/---/layout: $layout\\n---/" $tag_file
|
||||
updated=1
|
||||
fi
|
||||
|
||||
if [ $updated = 0 ]; then
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
done
|
29
_posts/2011-05-12-ethical-hacking-2011.markdown
Normal file
29
_posts/2011-05-12-ethical-hacking-2011.markdown
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Ethical Hacking 2012"
|
||||
date: 2011-05-12 20:54:42
|
||||
tags: [conference]
|
||||
permalink: /blog/2011/5/12/ethical-hacking-2011
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Today I went to the Ethical Hacking conference with my boss. It was my first
|
||||
appearance at such conferences, but I hope there will be more. Although we
|
||||
just started to redesign our IT security infrastructure with a 90% clear goal,
|
||||
it was nice to hear that everything is vulnerable. I was thinking if we should
|
||||
sell all our IT equipments, fire all our colleagues (you know, to prevent
|
||||
social engineering), and move to the South Americas to herd llamas or sheep,
|
||||
so the only danger would be some lurking pumas or jaguars. Or I simply leave
|
||||
my old background image on my desktop, from the well-known game, which says:
|
||||
Trust is a weakness.
|
||||
|
||||
Anyways, the conference was really nice. We heard about the weaknesses of
|
||||
Android, Oracle, and even FireWire. They showed some demos about everything,
|
||||
exploited some free and commercial software with no problem at all. We have
|
||||
seen how much power the virtualisation admin has (although I think it can be
|
||||
prevented, but I’m not sure yet). However, in the end, we could see that the
|
||||
Cloud is secure (or at least it can be, in a few months or so), so I’m not
|
||||
totally pessimistic. See you next time at Hacktivity!
|
@ -0,0 +1,88 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Gentoo hardened desktop with GNOME 3 – Round one"
|
||||
date: 2011-05-12 20:32:41
|
||||
tags: [gentoo, gnome3, selinux]
|
||||
permalink: /blog/2011/5/12/gentoo-hardened-desktop-with-gnome-3-round-one
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
After having some hard times with Ubuntu (upgrading from 10.10 to 11.04), I
|
||||
decided to switch back to my old friend, Gentoo. As I’m currently learning
|
||||
about Linux hardening, I decided to use the new SELinux profile, which
|
||||
supports the v2 reference policy.
|
||||
|
||||
Installation was pretty easy, using the [Gentoo x86
|
||||
Handbook](http://www.gentoo.org/doc/hu/handbook/handbook-x86.xml). This profile
|
||||
automatically turns on the `USE=selinux` flag (so does the old SELinux
|
||||
profile), but deprecated `FEATURE=loadpolicy` (which is turned on by the
|
||||
profile, so portage will complain about it until you disable it in
|
||||
`/etc/make.conf`).
|
||||
|
||||
For the kernel, I chose `hardened-sources-2.6.37-r7`. This seems to be recent
|
||||
enough for my security testing needs. I turned on both SELinux, PaX and
|
||||
grsecurity. So far, I have no problem with it, but I don’t have X installed
|
||||
yet, which will screw up things for sure.
|
||||
|
||||
After having those hard times with Ubuntu mentioned before, I decided not to
|
||||
install Grub2 yet, as it renders things unusable (eg. my Windows 7
|
||||
installation, which I sometimes need at the office). So I installed Grub 0.97
|
||||
(this is the only version marked as stable, as I remember), touched
|
||||
`/.autorelabel`, and reboot.
|
||||
|
||||
My first mistake was using an UUID as the root device on the kernel parameter
|
||||
list (I don’t want to list all the small mistakes like forgetting to include to
|
||||
correct SATA driver from my kernel and such). Maybe I was lame, but after
|
||||
including `/dev/sda5` instead of the UUID thing, it worked like…
|
||||
|
||||
Well, charm would not be the good word. For example, I forgot to install the
|
||||
lvm2 package, so nothing was mounted except my root partition. After I
|
||||
installed it with the install CD, I assumed everything will be all right, but
|
||||
I was wrong.
|
||||
|
||||
udev and LVM is a critical point in a hardened environment. udev itself
|
||||
doesn’t want to work without the `CONFIG_DEVFS_TEMPFS=y` kernel option, so I
|
||||
also had to change that. It seemed that it can be done without the install CD,
|
||||
as it compiled the kernel with no problems. However, when it reached the point
|
||||
when it compresses the kernel with gzip, it stopped with a `Permission denied`
|
||||
message (although it was running with root privileges).
|
||||
|
||||
The most beautiful thing in the hardened environment with Mandatory Access
|
||||
Control enabled) is that root is not a real power user any more by default.
|
||||
You can get this kind of messages many times. There are many tools to debug
|
||||
these, I will talk about these later.
|
||||
|
||||
So, my gzip needed a fix. After digging a bit on the Internet, I found that
|
||||
the guilty thing is text relocation, which can be corrected if gzip is
|
||||
compiled with PIC enabled. Thus, I turned on `USE=pic` flag globally, and
|
||||
tried to remerge gzip. Of course it failed, as it had to use gzip to unpack
|
||||
the gzip sources. So it did when I tried to install the PaX tools and gradm to
|
||||
turn these checks off. The install CD came to the rescue again, with which I
|
||||
successfully recompiled gzip, and with this new gzip, I compressed my new
|
||||
kernel, with which udev started successfully. So far, so good, let’s try to
|
||||
reboot!
|
||||
|
||||
Damn, LVM is still not working. So I decided to finally consult the Gentoo
|
||||
hardened guide. It says that the LVM startup scripts under `/lib/rcscripts/…`
|
||||
must be modified, so LVM will put its lock files under `/etc/lvm/lock` instead
|
||||
of `/dev/.lvm`. After this step and a reboot, LVM worked fine (finally).
|
||||
|
||||
The next thing was the file system labelling. SELinux should automatically
|
||||
relabel the entire file system at boot time whenever it finds the
|
||||
`/.autorelabel` file. Well, in my case it didn’t happen. After checking the
|
||||
[Gentoo Hardening](http://wiki.gentoo.org/wiki/Hardened_Gentoo) docs, I realised that the `rlpkg` program does exactly the same
|
||||
(as far as I know, it is designed specifically for Gentoo). So I ran `rlpkg`,
|
||||
and was kind of shocked. It says it will relabel ext2, ext3, xfs and JFS
|
||||
partitions. Oh great, no ext4 support? Well, consulting the forums and adding
|
||||
some extra lines to `/etc/portage/package.keywords` solved the problem (`rlpkg`
|
||||
and some dependencies had to have the `~x86` keyword set). Thus, `rlpkg`
|
||||
relabelled my file systems (I checked some directories with `ls -lZ`, it seemed
|
||||
good for me).
|
||||
|
||||
Now it seems that everything is working fine, except the tons of audit
|
||||
messages. Tomorrow I will check them with `audit2why` or `audit2allow` to see if
|
||||
it is related with my SELinux lameness, or with a bug in the policy included
|
||||
with Gentoo.
|
35
_posts/2011-05-13-zabbix-performance-tip.markdown
Normal file
35
_posts/2011-05-13-zabbix-performance-tip.markdown
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Zabbix performance tip"
|
||||
date: 2011-05-13 19:03:31
|
||||
tags: [zabbix, monitoring]
|
||||
permalink: /blog/2011/5/13/zabbix-performance-tip
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Recently I have switched from [MRTG](http://oss.oetiker.ch/mrtg/) + [Cacti](http://www.cacti.net/) + [Nagios](http://www.nagios.org/) + [Gnokii](http://www.gnokii.org/) to [Zabbix](http://www.zabbix.com/), and I
|
||||
must say I’m more than satisfied with it. It can do anything the former tools
|
||||
did, and much more. First of all, it can do the same monitoring as Nagios did,
|
||||
but it does much more fine. It can check several parameters within one
|
||||
request, so network traffic is kept down. Also, its web front-end can generate
|
||||
any kinds of graphs from the collected data, which took Cacti away. Also, it
|
||||
can do SNMP queries (v1-v3), so querying my switches’ port states and traffic
|
||||
made easy, taking MRTG out of the picture (I know Cacti can do it either, it
|
||||
had historical reasons we had both tools installed). And the best part: it can
|
||||
send SMS messages via a GSM modem natively, while Nagios had to use Gnokii.
|
||||
The trade-off is, I had to install Zabbix agent on all my monitored machines,
|
||||
but I think it worths the price. I even have had to install NRPE to monitor
|
||||
some parameters, which can be a pain on Windows hosts, while Zabbix natively
|
||||
supports Windows, Linux and Mac OS/X.
|
||||
|
||||
So I only had to create a MySQL database (which I already had for NOD32
|
||||
central management), and install Zabbix server. Everything went fine, until I
|
||||
reached about 1300 monitored parameters. MySQL seemed to be a bit slow on disk
|
||||
writes, so my Zabbix “queue” filled up in no time. After reading some forums,
|
||||
I decided to switch to PostgreSQL instead. Now it works like charm, even with
|
||||
the default Debian settings. However, I will have to add several more
|
||||
parameters, and my boss wants as many graphs as you can imagine, so I’m more
|
||||
than sure that I will have to fine tune my database later.
|
@ -0,0 +1,29 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Gentoo hardened desktop with GNOME 3 – Round two"
|
||||
date: 2011-05-18 10:28:14
|
||||
tags: [gentoo, gnome3, selinux]
|
||||
permalink: /blog/2011/5/18/gentoo-hardened-desktop-with-gnome-3-round-two
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
After several hours of `package.keywords`/`package.use` editing and package
|
||||
compiling, I managed to install GNOME 3 on my notebook. Well, I mean, the
|
||||
GNOME 3 packages. Unfortunately the fglrx driver didn’t seem to recognise my
|
||||
ATI Mobility M56P card, and the open source driver didn’t want to give me GLX
|
||||
support. When I finally found some clues on what should I do, I had to use my
|
||||
notebook for work, so I installed Fedora 14 on it. Then I realised that GNOME
|
||||
3 is already included in Rawhide (Fedora 15), so I quickly downloaded and
|
||||
installed that instead. Now I have to keep this machine in a working state for
|
||||
a few days, so I will learn SELinux stuff in its native environment.
|
||||
|
||||
When I installed Fedora 14, the first AVC message popped up after about ten
|
||||
minutes. That was a good thing, as I wanted to see `setroubleshoot` in action.
|
||||
However, in Fedora 15, the AVC bubbles didn’t show up even after a day. I
|
||||
raised my left eyebrow and said that’s impossible, SELinux must be disabled.
|
||||
And it’s not! It’s even in enforcing mode! And it works just fine. I like it,
|
||||
and I hope I will be able to get the same results with Gentoo if I can get
|
||||
back to testing…
|
@ -0,0 +1,41 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Citrix XenServer 5.5 vs. Debian 5.0 upgrade to 6.0"
|
||||
date: 2011-05-27 17:33:41
|
||||
tags: [citrix-xenserver, debian]
|
||||
permalink: /blog/2011/5/27/citrix-xenserver-vs-debian-5-0-upgrade-to-6-0
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Few weeks ago I’ve upgraded two of our Debian based application servers from
|
||||
5.0 to 6.0. Everything went fine, as the upgraded packages worked well with
|
||||
the 4.2 JBoss instances. For the new kernel we needed a reboot, but as the
|
||||
network had to be rebuilt, I postponed this reboot until the network changes.
|
||||
With the network, everything went fine again, we successfully migrated our
|
||||
mail servers behind a firewall. Also the Xen server (5.5.0, upgrade to 5.6
|
||||
still has to wait for a week or so) revolted well with some storage disks
|
||||
added. But the application servers remained silent…
|
||||
|
||||
After checking the console, I realised that they don’t have an active console.
|
||||
And when I tried to manually start them, XenServer refused with a message
|
||||
regarding pygrub.
|
||||
|
||||
To understand the problem, I had to understand how XenServer boots Debian. It
|
||||
reads the grub.conf on the first partition’s root or `/boot` directory, and
|
||||
starts the first option, without asking (correct me, if I’m mistaken
|
||||
somewhere). However, this pygrub thing can not parse the new, grub2 config.
|
||||
This is kinda frustrating.
|
||||
|
||||
For the first step, I quickly installed a new Debian 5.0 system from my
|
||||
template. Then I attached the disks of the faulty virtual machine, and mounted
|
||||
all its partitions. This way I could reach my faulty 6.0 system with a chroot
|
||||
shell, from which I could install the `grub-legacy` package instead of grub,
|
||||
install the necessary kernel and XenServer tools (which were missing from both
|
||||
machines somehow), then halt the rescue system, and start up the original
|
||||
instance.
|
||||
|
||||
Next week I will do an upgrade on the XenServer to 5.6.1. I hope no such
|
||||
problems will occur.
|
@ -0,0 +1,25 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Oracle Database “incompatible” with Oracle Linux?"
|
||||
date: 2011-05-27 17:53:31
|
||||
tags: [linux, oracle]
|
||||
permalink: /blog/2011/5/27/oracle-database-incompatible-with-oracle-linux
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Today I gave a shot to install [Oracle
|
||||
Linux](http://www.oracle.com/us/technologies/linux/overview/index.html). I thought I could easily install
|
||||
an Oracle DBA on it. Well, I was naive.
|
||||
|
||||
As only the 5.2 version is supported by XenServer 5.5, I downloaded that
|
||||
version of Oracle Linux. Installing it was surprisingly fast and easy, it
|
||||
asked almost nothing, and booted without any problems.
|
||||
|
||||
After this came the DBA, 10.2, which bloated an error message in my face
|
||||
saying that this is an unsupported version of Linux. Bah.
|
||||
|
||||
Is it only me, or is it really strange that Oracle doesn’t support their own
|
||||
distro?
|
@ -0,0 +1,22 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Proxy only non-existing files with mod_proxy and mod_rewrite"
|
||||
date: 2011-06-10 14:20:43
|
||||
tags: [apache]
|
||||
permalink: /blog/2011/6/10/proxy-only-non-existing-files-with-mod-proxy-and-mod-rewrite
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Today I got an interesting task. I had to upload some pdf documents to a site.
|
||||
The domain is ours, but we don’t have access to the application server that is
|
||||
hosting the page yet. Until we get it in our hands, I did a trick.
|
||||
|
||||
I enabled `mod_rewrite`, `mod_proxy` and `mod_proxy_http`, then added the following
|
||||
lines to my apache config:
|
||||
|
||||
{% gist 47680bfa44eb29708f20 redirect-non-existing.conf %}
|
||||
|
||||
I’m not totally sure it’s actually secure, but it works for now.
|
30
_posts/2011-09-18-inverse-of-sort.markdown
Normal file
30
_posts/2011-09-18-inverse-of-sort.markdown
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Inverse of `sort`"
|
||||
date: 2011-09-18 14:57:31
|
||||
tags: [linux, command-line]
|
||||
permalink: /blog/2011/9/18/inverse-of-sort
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I’m using \*NIX systems for about 14 years now, but it can still show me new
|
||||
things. Today I had to generate a bunch of random names. I’ve create a small
|
||||
perl script which generates permutations of some usual Hungarian first and
|
||||
last names, occasionally prefixing it with a ‘Dr.’ title or using double first
|
||||
names. For some reasons I forgot to include uniqueness check in the script.
|
||||
When I ran it in the command line, I realized the mistake, so I appended
|
||||
`| sort | uniq` to the command line. So I had around 200 unique names, but in
|
||||
alphabetical order, which was awful for my final goal. Thus, I tried shell
|
||||
commands like rand to create a random order, and when many of my tries failed,
|
||||
the idea popped in my mind (not being a native English speaker): “I don’t have
|
||||
to create «random order», but «shuffle the list». So I started typing `shu`,
|
||||
pressed Tab in the Bash shell, and voilà! `shuf` is the winner, it does just
|
||||
exactly what I need:
|
||||
|
||||
**NAME**
|
||||
shuf - generate random permutations
|
||||
|
||||
Thank you, Linux Core Utils! :)
|
@ -0,0 +1,16 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Why you should always test your software with production data"
|
||||
date: 2011-12-11 12:14:51
|
||||
tags: [development, testing, ranting]
|
||||
permalink: /blog/2011/12/11/why-you-should-always-test-your-software-with-production-data
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I’m writing a software for my company in PHP, using the Symfony 2 framework.
|
||||
I’ve finished all the work, created some sample data, it loaded perfectly. Now
|
||||
I put the whole thing into production and tried to upload the production data
|
||||
into it. Guess what… it didn’t load.
|
29
_posts/2012-03-20-php-5-4-released.markdown
Normal file
29
_posts/2012-03-20-php-5-4-released.markdown
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
layout: post
|
||||
title: "PHP 5.4 released"
|
||||
date: 2012-03-20 13:31:12
|
||||
tags: [php]
|
||||
permalink: /blog/2012/3/20/php-5-4-released
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
After a long time of waiting, PHP announced 5.4 release on 1 March (also,
|
||||
today they announced that they finally migrate to Git, which is sweet from my
|
||||
point of view, but it doesn’t really matter).
|
||||
|
||||
About a year ago we became very agressive towards a developer who created our
|
||||
internal e-learning system. Their database was very insecure, and they didn’t
|
||||
really follow industry standards in many ways. Thus, we forced them to move
|
||||
from Windows + Apache 2.0 + PHP 5.2 + MySQL 4.0 to Debian Linux 6.0 + Apache
|
||||
2.2 + PHP 5.3 + MySQL 5.1. It was fun (well, from our point of view), as their
|
||||
coders… well… they are not so good. The code that ran “smoothly” on the
|
||||
old system failed at many points on the new one. So they code and code, and
|
||||
write more code. And they still didn’t finish. And now 5.4 is here. Okay, I
|
||||
know it will take some time to get into the Debian repositories, but it’s
|
||||
here. And they removed `register_globals`, which will kill that funny code again
|
||||
at so many points that they will soon get to rewrite the whole code to make it
|
||||
work. And I just sit here in my so-much-comfortable chair, and laugh. Am I
|
||||
evil?
|
34
_posts/2012-03-27-fast-world-fast-updates.markdown
Normal file
34
_posts/2012-03-27-fast-world-fast-updates.markdown
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Fast world, fast updates"
|
||||
date: 2012-03-27 06:18:43
|
||||
tags: [linux]
|
||||
permalink: /blog/2012/3/27/fast-world-fast-updates
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
We live in a fast world, that’s for sure. When I first heard about Ubuntu
|
||||
Linux and their goals, I was happy: they gave a Debian to everyone, but in
|
||||
different clothes. It had fresh software in it, and even they gave support of
|
||||
a kind. It was easy to install and use, even if one had no Linux experience
|
||||
before. So people liked it. I’ve even installed it on some of my servers
|
||||
because of the new package versions that came more often. Thus I got an up to
|
||||
date system. However, it had a price. After a while, security updates came
|
||||
more and more often, and when I had a new critical update every two or three
|
||||
days, I’ve decided to move back to Debian. Fortunately I did this at the time
|
||||
of a new release, so I didn’t really loose any features.
|
||||
|
||||
After a few years passed, even Debian is heading this very same way. But as I
|
||||
see, the cause is not the same. It seems that upstream software is hitting
|
||||
these bugs, and even the Debian guys don’t have the time to check for them. At
|
||||
the time of a GNOME version bump (yes, GNOME 3 is a really big one for the
|
||||
UN\*X-like OSes), when hundreds of packages need to be checked, security bugs
|
||||
show off more often. On the other hand however, Debian is releasing a new
|
||||
security update every day (I had one on each of the last three days). This, of
|
||||
course, is good from one point of view as we get a system that is more secure,
|
||||
but most administrators don’t have maintenance windows this often. I can think
|
||||
of some alternatives like Fedora, but do I really have to change? Dear fellow
|
||||
developers, please code more carefully instead!
|
28
_posts/2012-06-14-wordpress-madness.markdown
Normal file
28
_posts/2012-06-14-wordpress-madness.markdown
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Wordpress madness"
|
||||
date: 2012-06-14 06:40:12
|
||||
tags: [wordpress, ranting]
|
||||
permalink: /blog/2012/6/14/wordpress-madness
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I’m a bit fed up that I had to install [MySQL](http://www.mysql.com/) on my
|
||||
server to have [Wordpress](http://wordpress.org/) working, so I’ve Googled a
|
||||
bit to find a solution for my pain. I found
|
||||
[this](http://codex.wordpress.org/Using_Alternative_Databases). I don’t know when
|
||||
this post was written, but I think it’s a bit out of date. I mean come on, PDO
|
||||
is the part of PHP for ages now, and they say adding a DBAL to the dependencies
|
||||
would be a project as large as (or larger than) WP itself. Well,
|
||||
yes, but PHP is already a dependency, isn’t it? Remove it guys, it’s too
|
||||
large!
|
||||
|
||||
Okay, to be serious… Having a heavily MySQL dependent codebase is a bad
|
||||
thing in my opinion, and changing it is no easy task. But once it is done, it
|
||||
would be a child’s play to keep it up to date, and to port WP to other
|
||||
database backends. And it would be more than enough to call it 4.0, and
|
||||
raising version numbers fast is a must nowadays (right, Firefox and Linux
|
||||
Kernel guys?)
|
@ -0,0 +1,28 @@
|
||||
---
|
||||
layout: post
|
||||
title: "SSH login FAILed on Red Had Enterprise Linux 6.2"
|
||||
date: 2012-06-18 18:28:45
|
||||
tags: [linux, selinux, ssh, red-hat]
|
||||
permalink: /blog/2012/6/18/ssh-login-failed-on-red-hat-enterprise-linux-6-2
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Now this was a mistake I should not have done…
|
||||
|
||||
About a month ago I have moved my AWS EC2 machine from Amazon Linux to RHEL
|
||||
6.2. This was good. I have moved all my files and stuff, recreated my own
|
||||
user, everything was just fine. Then I copied my
|
||||
[gitosis](https://github.com/tv42/gitosis) account (user `git` and its home
|
||||
directory). Then I tried to log in. It failed. I was blaming OpenSSH for a week
|
||||
or so, changed the config file in several ways, tried to change the permissions
|
||||
on `~git/.ssh/*`, but still nothing. Permission were denied, I was unable to
|
||||
push any of my development changes. Now after a long time of trying, I
|
||||
coincidently `tail -f`-ed `/var/log/audit/audit.log` (wanted to open `auth.log`
|
||||
instead) and that was my first good point. It told me that `sshd` was unable to
|
||||
read `~git/.ssh/authorized_keys`, which gave me the idea to run `restorecon` on
|
||||
`/home/git`. It solved the problem.
|
||||
|
||||
All hail SELinux and RBAC!
|
@ -0,0 +1,35 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Upgrades requiring a reboot on Linux? At last!"
|
||||
date: 2012-06-22 20:04:51
|
||||
tags: [linux]
|
||||
permalink: /blog/2012/6/22/upgrades-requiring-a-reboot-on-linux-at-last
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I’ve recently received an article on Google+ about Fedora’s new idea: package
|
||||
upgrades that require a reboot. The article said that Linux guys have lost
|
||||
their primary adoo: “Haha! I don’t have to reboot my system to install system
|
||||
upgrades!” My answer was always this: “Well, actually you should…”
|
||||
|
||||
I think this can be a great idea if distros implement it well. PackageKit was
|
||||
a good first step on this road. That software could easily solve such an
|
||||
issue. However, it is sooo easy to do it wrong. The kernel, of course, can not
|
||||
be upgraded online (or could it be? I have some theories on this subject,
|
||||
wonder if it can be implemented…), but other packages are much different.
|
||||
From the users’ point of view the best would be if the packages would be
|
||||
upgraded in the background seemlessly. E.g. PackageKit should check if the
|
||||
given executable is running. If not, it should upgrade it, while notifying the
|
||||
user like “Hey dude, don’t start Anjuta now, I’m upgrading it!”, or simply
|
||||
denying to start it. Libraries are a bit different, as PackageKit should check
|
||||
if any running executables are using the library. Meanwhile, PK should also
|
||||
keep a notification somewhere telling the users that some packages could be
|
||||
upgraded, but without stopping this-and-that, it can not be done.
|
||||
|
||||
I know these things are easier said than done. But I think (a) users should
|
||||
tell such ideas to the developers and (b) developers (mostly large companies,
|
||||
like Microsoft or Apple) should listen to them, and at least think of these
|
||||
ideas. Some users are not as stupid as they think…
|
@ -0,0 +1,80 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Some thoughts about that dead Linux Desktop"
|
||||
date: 2012-09-05 09:01:31
|
||||
tags: [linux]
|
||||
permalink: /blog/2012/9/5/some-thoughts-about-that-dead-linux-desktop
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
There were some arguments in the near past on [What Killed the Linux
|
||||
Desktop](http://tirania.org/blog/archive/2012/Aug-29.html). After reading many
|
||||
replies, like [Linus
|
||||
Torvalds’](http://www.zdnet.com/linus-torvalds-on-the-linux-desktops-popularity-problems-7000003641/),
|
||||
I have my own thoughts, too.
|
||||
|
||||
I know my place in the world, especially in the online community. I’m a Linux
|
||||
user for about 15 years and a Linux administrator for 10 years now, beginning
|
||||
with WindowMaker and something that I remember as GNOME without a version
|
||||
number. I have committed some minor code chunks and translations in some minor
|
||||
projects, so I’m not really into it from the “write” side (well, until now,
|
||||
since I have began to write this blog, and much more, but don’t give a penny
|
||||
for my words until you see it).
|
||||
|
||||
I’m using Linux since 2.2 and GNOME since 1.whatever. It’s nice that a program
|
||||
compiled years ago still runs on today’s Linux kernel, especially if you see
|
||||
old DOS/Windows software failing to start on a new Windows 7 machine. I
|
||||
understand Linus’ point that breaking external APIs is bad, and I think it can
|
||||
work well on the kernel’s level. But the desktop level is much different. As
|
||||
the Linux Desktop has such competitors (like OS/X and Windows’ Aero and Metro),
|
||||
they have to give something new to the users almost every year to keep up with
|
||||
them. Eye candies are a must (yes, of course my techy fellows, they are
|
||||
worthless, but users *need* it), and they can not be created without extending
|
||||
APIs. And the old API… well, it fades away fast. I don’t really understand
|
||||
however, why they have to totally disappear, like
|
||||
[GTK_DIALOG_NO_SEPARATOR](http://developer.gnome.org/gtk/stable/GtkDialog.html#GtkDialogFlags)
|
||||
in Gtk3. It could be replaced with a 0 value (e.g: it won’t do anything). This
|
||||
way my old Gtk2 program could compile with Gtk3 nicely. Also, there could be a
|
||||
small software that goes through your source code and warn you about such
|
||||
deprecated (and no-doer but still working) things. Porting applications between
|
||||
Gtk (and thus, GNOME) versions became a real pain, which makes less enthusiast
|
||||
programmers stop developing for Linux. Since I’m a GNOME guy for years, I can
|
||||
tell nothing about Qt and KDE, but for the GNOME guys, this is a bad thing. As
|
||||
of alternatives, there is Java. No, wait… it turned out recently that [it has
|
||||
several security
|
||||
bugs](http://www.theregister.co.uk/2012/08/31/critical_flaw_found_in_patched_java).
|
||||
Also it’s not that multiplatform as they say (I can’t find the article on
|
||||
that at the moment, but I have proof). Also, the JVMs out there eat up so much
|
||||
resources, which makes it a bit hard and expensive to use.
|
||||
|
||||
Also, I see another problem: those blasted package managers. RPM, DPKG,
|
||||
Portage, whatever. What the hell? Why are there so many? Why do developers
|
||||
reinvent the wheel? The nave is too small or there are to few spokes? Come on…
|
||||
we live in an open source world! Contribute to the one and only package manager
|
||||
(which one is that I don’t actually care)! I’m sure the two (three, many)
|
||||
bunches of develoeprs could make a deal. Thus, it could become better and
|
||||
“outsider” companies would be happier to distribute their software for Linux
|
||||
platforms.
|
||||
|
||||
And now that we get to the big companies. I don’t really understand them.
|
||||
nVidia and ATI made their own closed source drivers for Linux. Some other
|
||||
hardware vendors also write Linux drivers, and as the kernel API doesn’t really
|
||||
change, they will work for a long time. But what about desktop
|
||||
application vendors? Well, they try to stick to a desktop environment or two,
|
||||
and if they change too frequently, they stop developing for Linux, like Skype
|
||||
did (OK, maybe Skype has other reasons, but you see my point). But why? The
|
||||
main part for Linux programs is the Linux kernel and the basic userland like
|
||||
libc/stdlib++. If you write graphical software, it will have to use X-Windows.
|
||||
Yes, it’s much different in many ways, mostly because they have a… well… pretty
|
||||
ugly design by default. But still, it’s the same on every Linux distributions,
|
||||
as it became somewhat an industry standard, as it was already on the market
|
||||
back in the old UN\*X days. The protocol itself changed just like the Linux
|
||||
kernel: almost no change at all, just some new features.
|
||||
|
||||
So what kills the Linux desktop in my opinion is these constant wars inside,
|
||||
and the lack of support from the outside. Open Source is good, but until these
|
||||
(mostly the first) problems are not resolved, Linux Desktop can do nothing on
|
||||
the market. It’s a downward spiral hard to escape.
|
@ -0,0 +1,76 @@
|
||||
---
|
||||
layout: post
|
||||
title: "How to start becoming a web developer"
|
||||
date: 2012-09-07 18:12:12
|
||||
tags: [development, technology]
|
||||
permalink: /blog/2012/9/7/how-to-start-becoming-a-web-developer
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
A friend of mine asked me today how to become a web developer. It took me a
|
||||
while, but I made up a checklist. It’s short, but it’s enough for the first
|
||||
steps.
|
||||
|
||||
#### First of all, learn English
|
||||
|
||||
Well, if you read this, maybe this was a bad first point…
|
||||
|
||||
#### Choose a language and stick to it!
|
||||
|
||||
For the UN\*X/Linux line, there is PHP. It’s free, easy to learn, and has many
|
||||
free tools and documentations available. It can be used in a functional or an
|
||||
object-oriented way.
|
||||
|
||||
C# is another good way to start, but for the Windows line. It’s fully object-
|
||||
oriented, and the web is full of tutorials, how-tos and other resources.
|
||||
|
||||
#### Learn the basics of the system you are working on
|
||||
|
||||
To become a good developer, learn at least the basics of the system you are
|
||||
working on. Basic commands can always come in handy. Debugging (yes, you will
|
||||
do tons of bugs for sure) can become much easier if you know the huge set of
|
||||
tools provided by your OS. You should also try to develop in the chosen
|
||||
environment. Chose PHP? Get a Linux desktop! ASP.NET? Get a Windows.
|
||||
Everything will be much easier!
|
||||
|
||||
#### Learn the basics of the web server you are using
|
||||
|
||||
PHP can run on [Apache](http://httpd.apache.org/) (as a module), or any
|
||||
CGI-capable webserver, like [lighttpd](http://www.lighttpd.net/) or
|
||||
[nginx](http://nginx.org/) (well, it can also run on IIS, but trust me: you
|
||||
don’t want that). ASP.NET is designed for IIS, and although some scripts can
|
||||
be run under a mono-capable server, it should still stay there.
|
||||
|
||||
Whichever you choose, learn the basics! How to start and stop the service,
|
||||
basic configuration methods, modules/extensions, and so on. It’s more than sure
|
||||
that you will face some issues while developing, so it can never hurt.
|
||||
|
||||
#### Keep your versions under control
|
||||
|
||||
Version control is critical nowadays. It gives you a basic backup solution,
|
||||
can come in handy with debugging, and if you ever want to work in a team, you
|
||||
will badly need it.
|
||||
|
||||
Subversion is a bit out of date now, and it’s kind of hard to set up.
|
||||
|
||||
Git is no easy. You will have to learn a lot of stuff, but basicly it’s just
|
||||
another version control system. Just choose if you want to stick to
|
||||
merge-then-commit or rebase-then-commit, get a client, and get on the run.
|
||||
|
||||
Microsoft’s Team Foundation is another good way if you are working in a team.
|
||||
It provides several other features besides version controlling, and is well
|
||||
integrated into Visual Studio, which is highly recommended for Windows based
|
||||
development.
|
||||
|
||||
#### Choose an environment to work in
|
||||
|
||||
There are so many good tools out there. You should choose according to the
|
||||
language and OS on what you are working on. [Zend
|
||||
Studio](http://www.zend.com/en/products/studio) or
|
||||
[Netbeans](https://netbeans.org/) are both good tools for PHP development,
|
||||
while [Visual Studio](http://www.visualstudio.com/) is a best buy for Windows
|
||||
development. Both of these have many ups and downs, but once you get in touch
|
||||
with their deeper parts, you will like them.
|
19
_posts/2012-09-10-do-not-track-in-ie10-vs-apache.markdown
Normal file
19
_posts/2012-09-10-do-not-track-in-ie10-vs-apache.markdown
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Do-Not-Track in IE10 vs. Apache"
|
||||
date: 2012-09-10 20:22:32
|
||||
tags: [apache, technology]
|
||||
permalink: /blog/2012/9/10/do-not-track-in-ie10-vs-apache
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
[Apache developer decided not to accept Do-Not-Track headers from IE10
|
||||
users](http://arstechnica.com/security/2012/09/apache-webserver-updated-to-ignore-do-not-track-settings-in-ie-10/),
|
||||
because it’s enabled by default. So… if I install a plugin that hides the
|
||||
fact from the web server that I’m using IE10, I become eligible of using
|
||||
it. But if I do this, I simply became eligible because I consciously installed
|
||||
that addon, so I could actually use it without hiding the fact. Sorry if
|
||||
I’m a bit Philosoraptorish…
|
@ -0,0 +1,64 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Symfony 2 – Create role- and class-based ACLs with your roles coming from the ORM"
|
||||
date: 2012-09-16 18:39:25
|
||||
tags: [php, symfony]
|
||||
permalink: /blog/2012/9/16/symfony-2-create-role-and-class-based-acls-with-your-roles-coming-from-the-orm
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
During the last weeks I had some serious issues with one of my private Symfony
|
||||
2 projects. One of my goals was to create a dynamic security system, e.g my
|
||||
administrators wanted to create roles, and grant these roles access to
|
||||
different object types (classes) and/or objects.
|
||||
|
||||
So I have created a `User` entity, which implements `UserInterface` and
|
||||
`AdvancedUserInterface`, the latter for the possibility to enable/disable
|
||||
accounts and such. It had a `$roles` property, which was a `ManyToMany` relation
|
||||
to the `Role` entity, which implemented `RoleInterface`. Also I have created my
|
||||
own role hierarchy service that implements `RoleHierarchyInterface`.
|
||||
|
||||
So far so good, first tests. It soon turned out that if `User::getRoles()`
|
||||
returns a `DoctrineCollection` as it does by default, then the standard
|
||||
|
||||
{% gist 883ace4f35e440f6fe0f WhatEver.php %}
|
||||
|
||||
doesn’t work. I know, it should not be hard coded, as my roles and permission
|
||||
tables are dynamic, I have just tested. So I fixed my `User` entity so
|
||||
`getRoles()` returns an array of `Role` objects instead of the
|
||||
`DoctrineCollection`. Also I implemented a `getRolesCollection()` method to
|
||||
return the original collection, but I think it will never be used.
|
||||
|
||||
After that, I had to implement some more features so I put this task away.
|
||||
Then, I tried to create my first ACL.
|
||||
|
||||
{% gist 883ace4f35e440f6fe0f WhatEver2.php %}
|
||||
|
||||
I was about to check if the user who is logged in has an `OWNER` permission on
|
||||
the `User` class.
|
||||
|
||||
{% gist 883ace4f35e440f6fe0f WhatEver3.php %}
|
||||
|
||||
The ACL was defined based on a role, so everyone who had the `ROLE_ADMIN` role
|
||||
should gain access to the user listing page. But they didn’t. It took several
|
||||
weeks to find the cause, I have put it on
|
||||
[stackoverflow](http://stackoverflow.com/questions/12057795/symfony-2-1-this-getsecurity-context-isgrantedrole-admin-returns-fa)
|
||||
and the Symfony Google Group, but no usable answers.
|
||||
|
||||
Then I went off for debugging. Setting up NetBeans for xdebug-based PHP
|
||||
debugging was real fun under Fedora, but that’s another story. After a while I
|
||||
have found that Symfony’s basic access decision manager checks for
|
||||
`$role->getRole()` only if `$role` is an instance of
|
||||
`Symfony\Component\Security\Core\Role\Role`, instead of checking if the object
|
||||
implements `Symfony\Component\Security\Core\Role\RoleInterface`. So I’ve
|
||||
checked if the bug is already reported. It turned out that it was, and my
|
||||
solution was available in a specific commit about a year ago, but as [Johannes
|
||||
Schmitt commented, it would introduce a security
|
||||
issue](https://github.com/symfony/symfony/commit/af70ac8d777873c49347ac828a817a400006cbea),
|
||||
so it was reverted. Unfortunately neither Johannes Schmitt, nor Fabien
|
||||
Potencier (nor anyone else) could (or wanted) to tell about this issue. So the
|
||||
final (and somewhat hack-like) solution was to extend
|
||||
`Symfony\Component\Security\Core\Role\Role`. And boom! It worked.
|
25
_posts/2012-10-07-smsgateway-and-smssender.markdown
Normal file
25
_posts/2012-10-07-smsgateway-and-smssender.markdown
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
layout: post
|
||||
title: "SmsGateway and SmsSender"
|
||||
date: 2012-10-07 00:10:26
|
||||
tags: [development, php, symfony]
|
||||
permalink: /blog/2012/10/7/smsgateway-and-smssender
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I have just uploaded my SmsGateway, SmsSender and SmsSenderBundle packages to
|
||||
[GitHub](http://github.com/gergelypolonkai) and
|
||||
[Packagist](http://packagist.org). I hope some of you will find it useful.
|
||||
|
||||
* SmsGateway
|
||||
* [GitHub](https://github.com/gergelypolonkai/smsgateway)
|
||||
* [Packagist](https://packagist.org/packages/gergelypolonkai/smsgateway)
|
||||
* SmsSender
|
||||
* [GitHub](https://github.com/gergelypolonkai/smssender)
|
||||
* [Packagist](https://packagist.org/packages/gergelypolonkai/smssender)
|
||||
* SmsSenderBundle
|
||||
* [GitHub](https://github.com/gergelypolonkai/smssender-bundle)
|
||||
* [Packagist](https://packagist.org/packages/gergelypolonkai/smssender-bundle)
|
@ -0,0 +1,20 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Changing the session cookie’s name in Symfony 2"
|
||||
date: 2012-10-13 12:49:28
|
||||
tags: [symfony, development]
|
||||
permalink: /blog/2012/10/13/changing-the-session-cookie-s-name-in-symfony-2
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I have a development server, on which I have several Symfony 2.x projects under
|
||||
the same hostname in different directories. Now I’m facing a funny problem
|
||||
which is caused by that the cookies Symfony places for each of my projects have
|
||||
the same name.
|
||||
|
||||
To change this, you will have to modify the `config.yml` file like this:
|
||||
|
||||
{% gist c695670ecca2809f7c93 %}
|
@ -0,0 +1,22 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Symfony 2 Configuration – Array of associative arrays"
|
||||
date: 2012-12-20 12:03:23
|
||||
tags: [php, symfony]
|
||||
permalink: /blog/2012/12/20/symfony-2-configuration-array-of-associative-arrays
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Few days ago I have struggled with a problem using Symfony2 configuration. I
|
||||
wanted to add the following kind of configuration to `config.yml`:
|
||||
|
||||
{% gist 30440e25f7a447730064 config.yml %}
|
||||
|
||||
The problem was that the stuff under `transitions` is dynamic, so those
|
||||
`hc_cba` and `cba_hc` tags can be pretty much anything. After hitting many
|
||||
errors, I came to the solution:
|
||||
|
||||
{% gist 30440e25f7a447730064 DynarrayConfiguration.php %}
|
14
_posts/2013-01-05-development-man-pages-on-fedora.markdown
Normal file
14
_posts/2013-01-05-development-man-pages-on-fedora.markdown
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Development man pages on Fedora"
|
||||
date: 2013-01-05 18:20:41
|
||||
tags: [development, fedora]
|
||||
permalink: /blog/2013/1/5/development-man-pages-on-fedora
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
If you use Fedora (like me), and can’t find the development manual pages for
|
||||
e.g. `printf(3)` (like me), just `yum install man-pages` (like me).
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Registering an enum type in GLib’s type system"
|
||||
date: 2013-01-06 02:34:03
|
||||
tags: [c, development, glib]
|
||||
permalink: /blog/2013/1/6/registering-an-enum-type-in-glib-s-type-system
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I faced a problem in my [GLib](https://developer.gnome.org/glib/) self-teaching
|
||||
project, [wMUD](https://github.com/gergelypolonkai/wmud) today. I wanted to
|
||||
register a signal for a `GObject`, whose handler should accept two `enum`
|
||||
parameters for which I had to register a new `GEnum` type in the `GObject` type
|
||||
system. However, the [documentation on this
|
||||
feature](https://developer.gnome.org/gobject/unstable/gtype-non-instantiable.html)
|
||||
(thanks for pointing out goes to hashem on `#gnome-hackers`) is not… uhm…
|
||||
obvious. Making the long story short, I have checked with the `GIO` sources for
|
||||
an example, and using that, I have created this small, working chunk:
|
||||
|
||||
{% gist 47794b6fb94484f8160b client-state.h %}
|
||||
|
||||
{% gist 47794b6fb94484f8160b client-state.c %}
|
||||
|
||||
Still, it can be made more perfect by using the
|
||||
[glib-mkenums](http://developer.gnome.org/gobject/stable/glib-mkenums.html)
|
||||
tool. I will read through the GLib Makefiles tomorrow for some hints on
|
||||
this.
|
||||
|
||||
Edit: you can find the glib-mkenums solution [here]({% post_url 2014-08-16-registering-an-enum-type-in-glib-glib-mkenums-magic %}).
|
17
_posts/2013-01-14-git-rm-cached-madness.markdown
Normal file
17
_posts/2013-01-14-git-rm-cached-madness.markdown
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
layout: post
|
||||
title: "git rm --cached madness"
|
||||
date: 2013-01-14 21:38:00
|
||||
tags: [development, git]
|
||||
permalink: /blog/2013/1/14/git-rm-cached-madness
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I have recently learned about `git rm --cached`. It’s a very good tool, as it
|
||||
removes a file from tracking, without removing your local copy of it. However,
|
||||
be warned that if you use `git pull` in another working copy, the file will be
|
||||
removed from there! If you accidentally put the configuration of a production
|
||||
project, and remove it on your dev machine, it can cause a lot of trouble ;)
|
@ -0,0 +1,52 @@
|
||||
---
|
||||
layout: post
|
||||
title: "JMS\\DiExtraBundle’s GrepPatternFinder – grep exits with status code 2 on Fedora 18"
|
||||
date: 2013-01-17 00:32:12
|
||||
tags: [fedora, selinux, symfony]
|
||||
permalink: /blog/2013/1/17/jms-diextrabundle-s-greppatternfinder-grep-exits-with-status-code-2-on-fedora-18
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Yesterday I’ve upgraded my development machines from Fedora 17 to Fedora
|
||||
18. Although it went well, my [Symfony](http://symfony.com/) projects stopped
|
||||
working with a message like this:
|
||||
|
||||
RuntimeException: Command "/usr/bin/grep --fixed-strings --directories=recurse --devices=skip --files-with-matches --with-filename --color=never --include=*.php 'JMS\DiExtraBundle\Annotation'
|
||||
'/var/www/html/gergelypolonkaiweb/app/../src'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/jms/aop-bundle/JMS/AopBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/doctrine/doctrine-migrations-bundle/Doctrine/Bundle/MigrationsBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/friendsofsymfony/jsrouting-bundle/FOS/JsRoutingBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/avalanche123/imagine-bundle/Avalanche/Bundle/ImagineBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/genemu/form-bundle/Genemu/Bundle/FormBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/src/GergelyPolonkai/FrontBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/src/GergelyPolonkai/GeshiBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle'
|
||||
'/var/www/html/gergelypolonkaiweb/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle'" exited with non-successful status code "2".
|
||||
|
||||
After getting through my logs and such, I’ve finally found out that the new
|
||||
SELinux policy is causing the trouble together with git. Eventually, my
|
||||
`.git/logs` directory is tagged as `unconfined_u:object_r:httpd_log_t:s0`.
|
||||
`httpd_log_t` type is not readable by the `system_u:system_r:httpd_t:s0` user,
|
||||
which makes `/usr/bin/grep` throw an access denied error. To fix this, I needed
|
||||
to do
|
||||
|
||||
semanage fcontext -a -t httpd_sys_content_t '/var/www(/.*)?/\.git/logs(/.*)?'
|
||||
|
||||
as root. This makes `.git` directories readable for the httpd process, thus,
|
||||
for `grep`. The optimal solution would be to tell `GrepPatternFinder` to ignore
|
||||
version control stuff, so the `httpd` process would have no access to them at
|
||||
all. Also, in production, removing the `.git` or `.svn` directories could be a
|
||||
good idea.
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
layout: post
|
||||
title: "mount: device or resource busy after enabling multipath"
|
||||
date: 2013-02-19 23:09:05
|
||||
tags: [linux, heartbeat-cluster]
|
||||
permalink: /blog/2013/2/19/mount-device-or-resource-busy-after-enabling-multipath
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
We have a heartbeat cluster with two nodes. It has been running for several
|
||||
months without problems. The shared storage is on an IBM DS3400, on which we
|
||||
have a large volume formatted with ext4.
|
||||
|
||||
Today I decided to reboot the active node for security reasons. So I’ve
|
||||
switched to the passive node, which failed at the first step: it was unable to
|
||||
mount the storage (`/dev/sda1`). After whining for a few moments, I tried to
|
||||
mount it by hand, which told me
|
||||
|
||||
/dev/sda1 already mounted or /data is busy
|
||||
|
||||
I’ve quickly made sure that none of that was true. After checking
|
||||
this-and-that, it turned out that the passive node had `multipathd` running, so
|
||||
I looked under `/dev/mapper`, and found two symlinks there, `<long-long WWN>`
|
||||
and `<long-long WWN>-part1`. As the partition table and the disk size was the
|
||||
same as on `/dev/sda`, I tried to
|
||||
|
||||
mount /dev/<long-long WWN>-part1 /data
|
||||
|
||||
and voilà! It worked like charm!
|
@ -0,0 +1,27 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Why I stopped using annotation based routing in Symfony today"
|
||||
date: 2013-02-27 23:10:24
|
||||
tags: [development, symfony]
|
||||
permalink: /blog/2013/2/27/why-i-stopped-using-annotation-based-routing-in-symfony-today
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I have read several opinions about routing configuration in Symfony. I stayed
|
||||
with annotation based routing as it was convinient for me to see the URL right
|
||||
above the controller action. This was because by just checking the URL, I
|
||||
remembered the controlling code, as they always were fresh ones. Well, until
|
||||
today.
|
||||
|
||||
I had to take a look into an old (Sf 2.0, last commit was about 3 months ago)
|
||||
project of mine. In the same run I’ve upgraded the whole project to 2.2 (it was
|
||||
a fast one, thanks for [JMikola@GitHub](https://github.com/jmikola) for the
|
||||
quick reply on my issue with
|
||||
[JmikolaJsAssetsHelperBundle](https://github.com/jmikola/JmikolaJsAssetsHelperBundle)
|
||||
again!). After that I went on to the requested change. Now, finding a route in
|
||||
about 40 controller files spread between 3 bundles can really be a pain! So
|
||||
I’ve finished with annotation based routing. It’s still a nice feature, it’s
|
||||
simply not for me.
|
62
_posts/2013-03-01-programming-as-i-see-it.markdown
Normal file
62
_posts/2013-03-01-programming-as-i-see-it.markdown
Normal file
@ -0,0 +1,62 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Programming, as I see it"
|
||||
date: 2013-03-01 23:32:35
|
||||
permalink: /blog/2013/3/1/programming-as-i-see-it
|
||||
published: false
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Since my age of around 11, I write code. I began with BASIC, which is,
|
||||
well, the most basic language I have ever seen. Simply writing
|
||||
<code>10 PRINT "Hello World!"</code> does the job (with Assembly it
|
||||
would be tens of lines as I recall). Then I moved to Pascal, then
|
||||
Delphi (which is basically the same thing). The next step was a bit
|
||||
longer, as I started learning more languages after this, like Perl
|
||||
(for dynamic web pages), C (for desktop applications), TCL (for
|
||||
eggdrop programming. Yes, I might have been a weird kid), PHP (again,
|
||||
for dynamic web pages. It was becoming mainstream back then).
|
||||
|
||||
Many of my classmates looked down on me, as they thought I was a geek (hell I
|
||||
was, but I wouldn’t have confessed it then), and called me a nerd. For a few
|
||||
months maybe I was depressed, but after that I realised that this is the thing
|
||||
I want to do in my life, this is the thing I’m good at.
|
||||
|
||||
Most people I ask why don’t they code say “it’s too hard”. I’ve attended some
|
||||
courses (both online and offline, and I was like “Whoa! Coding is extremely
|
||||
hard! What the hell! I will never learn it!”, but right after the course I
|
||||
realised that everything is just fine, I can still write programs, and it’s
|
||||
eeeeasy. So then, what’s the problem?
|
||||
|
||||
After looking through many course papers, I found that most teachers do it
|
||||
totally wrong. A programming language is just that: a language. You don’t start
|
||||
learning Spanish by going into a classic literature conference in Madrid and
|
||||
doing a speech, but learn the basic vocabulary and grammar. The same goes for
|
||||
coding. You learn the vocabulary (the basic commands or keywords) and grammar
|
||||
(syntax). I had several ideas how this could be taught, just didn’t have the
|
||||
background to do it.
|
||||
|
||||
The idea of teaching programming lingers in my head for years now, and a few
|
||||
days ago, I’ve bumped into [this
|
||||
video](https://www.youtube.com/watch?v=dU1xS07N-FA). So it seems that
|
||||
technology superstars like Bill Gates and Mark Zuckerberg wants to do the same.
|
||||
Maybe they don’t have enough high quality coders at hand. Well of course,
|
||||
if teachers make it awfully hard to learn it! So a bunch of guys sat together
|
||||
and created [code.org](http://www.code.org/) to achieve my old dream. I like
|
||||
the idea. And although I have almost no visitor on this blog of mine, allow me
|
||||
to give you a few points on how I see programming.
|
||||
|
||||
#### Great learning process
|
||||
|
||||
When you write programs, especially during the first years, you adapt a new way
|
||||
of thinking and learning. If you learn it as an adult, it can be a bit of a
|
||||
pain, but as a child, it’s easy as learning how the wheels of those little cars
|
||||
spin).
|
||||
|
||||
#### A job
|
||||
|
||||
#### Art
|
||||
|
||||
#### Magic
|
@ -0,0 +1,38 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Fedora can’t change Active Directory password via kpasswd"
|
||||
date: 2013-03-05 08:55:04
|
||||
tags: [fedora, kerberos, active-directory]
|
||||
permalink: /blog/2013/3/5/fedora-can-t-change-active-directory-password-via-kpasswd
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I wanted to change my AD password today. As the AD is actually a Kerberos
|
||||
server, I was pretty sure that `kpasswd` will do the trick. However, `kpasswd`
|
||||
output looked like this:
|
||||
|
||||
$ kpasswd
|
||||
Password for polonkai.gergely@EXAMPLE.LOCAL:
|
||||
Enter new password:
|
||||
Enter it again:
|
||||
kpasswd: Cannot find KDC for requested realm changing password
|
||||
|
||||
I’ve checked `kinit` and `klist`, everything looked fine. After a while it came
|
||||
to my mind that password changing is done through the kadmin server, not
|
||||
through the KDC. It seems that when I set up the Active Directory membership,
|
||||
the `admin_server` directive is not get written to `krb5.conf`. So all I had to
|
||||
do was to put
|
||||
|
||||
admin_server = ad.example.local
|
||||
|
||||
in that file, and voilà!
|
||||
|
||||
$ kpasswd
|
||||
Password for polonkai.gergely@EXAMPLE.LOCAL:
|
||||
Enter new password:
|
||||
Enter it again:
|
||||
Password changed.
|
||||
|
17
_posts/2013-03-05-haversine-in-mysql.markdown
Normal file
17
_posts/2013-03-05-haversine-in-mysql.markdown
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Haversine in MySQL"
|
||||
date: 2013-03-05 12:49:28
|
||||
permalink: /blog/2013/3/5/haversine-in-mysql
|
||||
tags: [mysql, development]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Just insert it in your database, feed them two Google coordinates, and you get
|
||||
the distance in kilometres. If you happen to need it in miles, change the
|
||||
constant `12756.200` in the `RETURN` row to `7922.6` instead.
|
||||
|
||||
{% gist bdad1cf2d410853bef35 %}
|
28
_posts/2013-03-13-dvorak-and-me.markdown
Normal file
28
_posts/2013-03-13-dvorak-and-me.markdown
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Dvorak and me"
|
||||
date: 2013-03-13 21:20:13
|
||||
tags: [linux]
|
||||
permalink: /blog/2013/3/13/dvorak-and-me
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
A few months ago I have decided to switch to the Dvorak layout. After using
|
||||
QWERTY (well, QWERTZ, to be precise) for almost 17 years, it was a hard
|
||||
decision, but now I think it worthed the try. I started with the UK (Dvorak
|
||||
with UK punctuation) layout, and in about four weeks, I’ve almost reached my
|
||||
original typing speed. Today I have modified the Hungarian xkb definitions file
|
||||
to add the Hungarian accended letters like ű to the layout, so I don’t have to
|
||||
use dead keys anymore (which apparently turned out to be a problem, as the
|
||||
Linux version of Java doesn’t support dead keys at all).
|
||||
|
||||
Best thing is, as I never learned proper 10-finger typing, but learned Dvorak
|
||||
that way, I can switch between QWERTY and Dvorak more or less painlessly (about
|
||||
10 minutes of confusion, so to say).
|
||||
|
||||
Conclusion: I don’t know yet if this was actually a good decision, but it
|
||||
wasn’t bad, after all. But seeing people’s faces when they try to type on my
|
||||
machine totally worths it.
|
28
_posts/2013-04-09-renaming-a-symfony-2-bundle.markdown
Normal file
28
_posts/2013-04-09-renaming-a-symfony-2-bundle.markdown
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Renaming a Symfony 2 bundle"
|
||||
date: 2013-04-09 22:29:48
|
||||
tags: [development, symfony]
|
||||
permalink: /blog/2013/4/9/renaming-a-symfony-2-bundle
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Today I’ve realised that the name I gave to one of my Symfony 2 bundles should
|
||||
be something else. To rename a bundle, one must do four things (at least).
|
||||
|
||||
1. Change the namespace from `Vendor\OldBundle` to `Vendor\NewBundle` in every
|
||||
PHP class (sounds like pain? It is…)
|
||||
1. Change the name of files and classes. Some files under
|
||||
`src/Vendor/OldBundle` (and the classes in them) contain the name of the
|
||||
bundle, like `OldBundle/DependencyInjection/VendorOldBundleExtension.php`
|
||||
and `OldBundle/VendorOldBundle.php`. You should rename them, or Symfony
|
||||
won’t find the classes defined in them! When done, rename the whole bundle
|
||||
directory either.
|
||||
1. Change the configuration files accordingly, including `AppKernel.php`. These
|
||||
config files are usually `routing.yml`, `services.yml`, and in some cases,
|
||||
`config.yml`
|
||||
1. Change the references in other parts of your code. A `grep OldBundle .` will
|
||||
usually help…
|
@ -0,0 +1,111 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Installing OTRS in Fedora 18 with SELinux enabled"
|
||||
date: 2013-05-06 06:01:52
|
||||
tags: [fedora, selinux, otrs]
|
||||
permalink: /blog/2013/5/6/installing-otrs-in-fedora-18-with-selinux-enabled
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I’ve read somewhere in an OTRS installation howto that if you want to install
|
||||
OTRS, you will have to disable SELinux. Well, I won’t.
|
||||
|
||||
During the last few months, I have been using Fedora 18 with SELinux on all of
|
||||
my desktop machines and on my notebook, and I had no problems at all.
|
||||
Meanwhile I got familiar with SELinux itself, and got used to solving problems
|
||||
caused by it. So I started `tail -f /var/log/httpd/error_log` in one terminal
|
||||
(to see if something Apache related thing appears),
|
||||
`tail -f /var/log/audit/audit.log` in another (to see errors caused by
|
||||
SELinux), opened the admin manual at the installation chapter, took a deep
|
||||
breath, and went on.
|
||||
|
||||
Throughout this article, I will refer to OTRS 3.2.6 as OTRS and Fedora 18
|
||||
(with only “stock” repositories) as Fedora. I assume that you have already
|
||||
installed OTRS in a non-SELinux environment before, and that you have at least
|
||||
some basic knowledge about SELinux, MAC, RBAC, and all the like. I’m
|
||||
installing OTRS in `/opt/otrs`, so if you install it somewhere else, you will
|
||||
have to modify the paths below. Also, if you happen to install under
|
||||
`/var/www` (I wouldn’t recommend it), that directory already has the
|
||||
`httpd_sys_content_t` type, so you won’t have to set it explicitly.
|
||||
|
||||
As the first step I have unpacked the archive to `/opt/otrs`. This directory
|
||||
is the OTRS default, many config files have it hardcoded, and changing it is
|
||||
no easy task.
|
||||
|
||||
Running `otrs.CheckModules.pl` gave me a list of missing perl modules. Red Hat
|
||||
and Fedora makes it easy to install these, as you don’t have to know the RPM
|
||||
package name, just the perl module name:
|
||||
|
||||
yum install 'perl(Crypt::SSLeay)' \
|
||||
'perl(DBD::Pg)' \
|
||||
'perl(GD)' \
|
||||
'perl(JSON::XS)' \
|
||||
'perl(GD::Text)' \
|
||||
'perl(GD::Graph)' \
|
||||
'perl(Mail::IMAPClient)' \
|
||||
'perl(Net::DNS)' \
|
||||
'perl(PDF::API2)' \
|
||||
'perl(Text::CSV_XS)' \
|
||||
'perl(YAML::XS)'
|
||||
|
||||
I also needed to install `mod_perl`. Although `otrs.CheckModules.pl` didn’t
|
||||
mention it, the default settings use syslog as the logging module, so unless
|
||||
you change it in `Config.pm`, you will also need to install
|
||||
`'perl(Unix::Syslog)'`, either.
|
||||
|
||||
The default SELinux policy doesn’t permit any network connection to be
|
||||
initiated by Apache httpd. As OTRS needs to connect to its database, you
|
||||
need to enable it explicitly. In older distributions, the
|
||||
`httpd_can_network_connect` was the SELinux boolean for this, but recent
|
||||
installations also have a `httpd_can_network_connect_db` flag. As far as I
|
||||
know, this enables all network connections to the well-known database
|
||||
servers’ default port, but I will have to check for it. For me, with a
|
||||
MySQL listening on its standard port, the
|
||||
`setsebool httpd_can_network_connect_db=1` command just did it.
|
||||
|
||||
With SELinux enabled, Apache won’t be able to read anything that’s not
|
||||
marked with the `httpd_sys_content_t` type, nor write anywhere without the
|
||||
`httpd_sys_rw_content_t` type. The trivial, quick and dirty solution is to
|
||||
label all the files as `httpd_sys_rw_content_t`, and let everything go.
|
||||
However, the goal of SELinux is just the opposite of this: grant access
|
||||
only to what is really needed. After many trial-and-error steps, it finally
|
||||
turned out that for OTRS to work correctly, you must set
|
||||
|
||||
* `httpd_sys_content_t`
|
||||
* on `/opt/otrs/var/httpd/htdocs`
|
||||
* `httpd_script_exec_t`
|
||||
* on `/opt/otrs/bin/cgi-bin`
|
||||
* `httpd_sys_rw_content_t`
|
||||
* on `/opt/otrs/Kernel`
|
||||
* on `/opt/otrs/var/sessions`
|
||||
* on `/opt/otrs/var/log` (unless you use syslog for logging)
|
||||
* on `/opt/otrs/var/packages` (this is used only when you download an .opm
|
||||
package)
|
||||
* on `/opt/otrs/var/stats`
|
||||
* on `/opt/otrs/var/tmp`
|
||||
* on `/opt/otrs/bin` (I wonder why this is required, though)
|
||||
|
||||
To do this, use the following command:
|
||||
|
||||
# semanage fcontext -a -t <context> <directory regex>
|
||||
|
||||
Where `<directory regex>` is something like `/opt/otrs/Kernel(/.*)?`. When
|
||||
this is done, all you have to do is running `restorecon -vR /opt/otrs` so
|
||||
it will relabel everything with the correct types (you can omit -v, I just
|
||||
like to see what my software does).
|
||||
|
||||
The last thing I faced is that Fedora is more restrictive on reading
|
||||
directories other than `/var/www`. It has a `Require all denied` on
|
||||
`<Directory />`, and a `Require all granted` on `<Directory /var/www>`, so
|
||||
`/opt/otrs/var/httpd/htdocs` will throw a
|
||||
`403 Forbidden (client denied by server configuration)` error. To get rid
|
||||
of this, I had to modify `scripts/apache2-httpd.include.conf` and add
|
||||
`Require all granted` to both the `cgi-bin` and `htdocs` directories.
|
||||
|
||||
As I will have to use OTRS in a production environment soon with SELinux
|
||||
enabled, it is more than sure that this list will change in the near future.
|
||||
As there are no official documentation on this (I haven’t find one yet), I
|
||||
have to do it with the trial-and-error way, so be patient!
|
30
_posts/2013-09-16-swe-glib-final-release.markdown
Normal file
30
_posts/2013-09-16-swe-glib-final-release.markdown
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
layout: post
|
||||
title: "SWE-GLib final release"
|
||||
date: 2013-09-16 21:37:17
|
||||
tags: [development, astrology]
|
||||
permalink: /blog/2013/9/16/swe-glib-final-release
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Few of you may know that I’m interested in astrology. About two months ago
|
||||
I have decided to create an astrologers’ software for the GNOME desktop.
|
||||
Since then, I have contacted Jean-André Santoni, who created a software
|
||||
called [Astrognome](https://code.google.com/p/astrognome/) some years ago.
|
||||
We exchanged some e-mails, and after several weeks of coding, I’m proud to
|
||||
present [SWE-GLib](https://github.com/gergelypolonkai/swe-glib) 1.0.1. This
|
||||
is “just” a library which wraps around [Swiss
|
||||
Ephemeris](http://www.astro.com/swisseph/), creating a nice GLib-ish
|
||||
interface around it. See the project page and the built-in GTK-Doc document
|
||||
for more information.
|
||||
|
||||
The astrologer’s software I’m writing will be
|
||||
[Astrognome](https://github.com/gergelypolonkai/astrognome) (thanks for
|
||||
Jean-André for letting me use the name). It is currently in pre-alpha
|
||||
status, but already utilizes SWE-GLib (it just can’t display the results
|
||||
yet). If you happen to be interested in astrology and/or Astrognome, fork
|
||||
the repository and contribute! You can also contact me (or open an
|
||||
enhancement issue on GitHub) if you have any ideas.
|
@ -0,0 +1,25 @@
|
||||
---
|
||||
layout: post
|
||||
title: "From Symfony to Django in two days"
|
||||
date: 2013-09-24 14:05:22
|
||||
tags: [development, symfony, django]
|
||||
permalink: /blog/2013/9/24/from-symfony-to-django-in-two-days
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I was a Python hater for a long time, although I can’t really tell why. It
|
||||
didn’t fit in my mind, maybe. I was programming in BASIC, Pascal (none of
|
||||
these would come to my mind, though), C, PHP, Perl, JavaScript, and
|
||||
different shell “languages” like awk, sed or bash.
|
||||
|
||||
After I could not fit my next Symfony app on my cloud server (it is pretty
|
||||
low on storage), I have decided to move slowly to Django. My first task was
|
||||
simple: transition my web page (this one) from PHP + Symfony 2 to Python +
|
||||
Django. The results: the “static” pages are already working, the blog
|
||||
listing is almost ready (some styling issues are still around), only
|
||||
tagging remains. And this is after about 6 hours of work. Oh, and the admin
|
||||
site is included with Django, so I don’t have to port that. I have also
|
||||
decided to finally integrate a comment feature in the Django version.
|
29
_posts/2013-11-05-first-impressions-of-windows-8.markdown
Normal file
29
_posts/2013-11-05-first-impressions-of-windows-8.markdown
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
layout: post
|
||||
title: "First impressions of Windows 8"
|
||||
date: 2013-11-05 08:14:50
|
||||
tags: [windows]
|
||||
permalink: /blog/2013/11/5/first-impressions-of-windows-8
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Many of you may know my commitment to Linux and Open Source Software. But this
|
||||
doesn’t mean I hate proprietary software like many others do. I think
|
||||
everything has its own place in the world, and this goes for software as well.
|
||||
|
||||
A few days ago I got my hands on a new notebook, thanks to my company. It was
|
||||
shipped with Windows 8 by default, and although I installed Fedora 19 in an
|
||||
instant (which went smoothlessly, even with Secure Boot enabled), I’ve decided
|
||||
to give a try to this new Windows Version.
|
||||
|
||||
Being a heavy Windows 7 user, my first thought was “What the hell is this?”
|
||||
But in a day, I got totally used to it. I don’t miss the Start button at all.
|
||||
The applications already installed were almost enough for me (I still need
|
||||
Office. Maybe I’ll also enroll for Office 365 later…), and the games are great
|
||||
and beautiful too. So overall, this new version may be totally different (by
|
||||
the looks), but it seems almost the same Windows as we know it. So if you
|
||||
don’t freak out by touching something new, go give it a try: don’t
|
||||
instant-remove 8 in favour of 7!
|
@ -0,0 +1,33 @@
|
||||
---
|
||||
layout: post
|
||||
title: "List Git branches and their remote tracking branches side by side"
|
||||
date: 2014-07-18 21:46:45
|
||||
tags: [git]
|
||||
permalink: /blog/2014/7/18/list-git-branches-and-their-remote-tracking-branches-side-by-side
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I had a hard time following my own branches in a project. They got pretty
|
||||
numerous, and I wasn’t sure if I pushed them to origin at all.
|
||||
`git branch -a` can list all the branches, including remote ones, but, as
|
||||
my list grew too big, it was impossible to follow it any more.
|
||||
|
||||
Thus, I have created a small script called git-branches-with-remotes, which
|
||||
does the work for me. Its only requirements are git (of course), and the
|
||||
`column` command, which is pretty obviously present on every POSIX
|
||||
compliant systems (even OSX).
|
||||
|
||||
{% gist 8af6a3e86b57dd4c250e %}
|
||||
|
||||
I just put it in my path, and `git branches-with-remotes` does the work!
|
||||
|
||||
Edit (16 August): I have added some code to mark the current branch (if any)
|
||||
with an asterisk. Also, I have put this script [in a
|
||||
gist](https://gist.github.com/gergelypolonkai/8af6a3e86b57dd4c250e).
|
||||
|
||||
Edit (26 February, 2015): It turns out that `git branch -vv` shows the same
|
||||
information and some more: it also shows if the branches are diverged, and the
|
||||
first line of the last commit’s message.
|
@ -0,0 +1,36 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Registering an enum type in GLib, glib-mkenums magic"
|
||||
date: 2014-08-16 15:10:54
|
||||
tags: [development, c, glib]
|
||||
permalink: /blog/2014/8/16/registering-an-enum-type-in-glib-glib-mkenums-magic
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
In [this
|
||||
post](/blog/2013/1/6/registering-an-enum-type-in-glib-s-type-system) I said
|
||||
I will get through the GLib Makefiles to add an enum type to GLib in a more
|
||||
sophisticated way.
|
||||
|
||||
In my other project,
|
||||
[SWE-GLib](https://github.com/gergelypolonkai/swe-glib) I already used this
|
||||
method. The following two rules in `Makefile.am` create `gswe-enumtypes.h`
|
||||
and `gswe-enumtypes.c`.
|
||||
|
||||
{% gist 1e2fdedb136de3ca67f0 Makefile %}
|
||||
|
||||
`$(GLIB_MKENUMS)` is set in `configure` with
|
||||
`AC_PATH_PROG([GLIB_MKENUMS], [glib-mkenums])`.
|
||||
|
||||
This approach requires the GNU Autotools (you can get rid of it by changing
|
||||
`$(GLIB_MKENUMS)` to the path to `glib-mkenums` binary), and two template
|
||||
files, one for the header and one for the code. `$(gswe_enum_headers)`
|
||||
contains a list of all the header files that have enum types defined
|
||||
throughout the project.
|
||||
|
||||
{% gist 1e2fdedb136de3ca67f0 gswe-enumtypes.h %}
|
||||
|
||||
{% gist 1e2fdedb136de3ca67f0 gswe-enumtypes.c %}
|
16
_posts/2014-09-17-nyanmacs.markdown
Normal file
16
_posts/2014-09-17-nyanmacs.markdown
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
layout: post
|
||||
title: "NyanMacs"
|
||||
date: 2014-09-17 12:45:42
|
||||
tags: [emacs]
|
||||
permalink: /blog/2014/9/17/nyanmacs
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I was a Vi/ViM user for years. For several reasons I had to change to Emacs
|
||||
now and then. And then, I found
|
||||
[this](https://www.emacswiki.org/emacs/NyanMode). I surrender. Emacs is
|
||||
just better. (And it’s working even in plain text mode without graphics)
|
@ -0,0 +1,25 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Rounding numbers to N decimals in Emacs"
|
||||
date: 2014-10-07 10:28:50
|
||||
tags: [emacs, development]
|
||||
permalink: /blog/2014/10/7/rounding-numbers-to-n-decimals-in-emacs
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I have recently faced a problem, where I had a bunch of SVG files with a
|
||||
large amount of fraction numbers in the path definitions. These images were
|
||||
displayed in small size, so this amount of precision was irrelevant, and
|
||||
these numbers took almost half of my SVG images’ size. So I created an
|
||||
Elisp defun to round these numbers to 2 decimals:
|
||||
|
||||
{% gist 9c721ceda6d3079b4f05 %}
|
||||
|
||||
This finds the first digit of the number under point (the cursor), and
|
||||
reduces its digits to the given amount (or the number given with `C-u`). It
|
||||
has some drawbacks, though, as it cannot handle exponential forms (e.g.
|
||||
`1e-1234`), but these were rare in my case, and its hard to iterate through
|
||||
all numbers. I will come over this latter problem soon(ish).
|
@ -0,0 +1,46 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Using Git bisect to find the first good commit"
|
||||
date: 2015-02-26 10:42:56
|
||||
tags: [git]
|
||||
permalink: /blog/2015/2/26/using-git-bisect-to-find-the-first-good-commit
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Few months ago we “implemented” a bug in our software, which was released
|
||||
to the customers. We continued development for two weeks when the first
|
||||
customer ticket arrived about the bug. We successfully reproduced it with
|
||||
the customer’s version, but not with the development sources; it turned out
|
||||
that one of the developers unconsciously fixed the bug. The devs spent some
|
||||
hours finding where the fix lied before coming to me like “There is
|
||||
`git-bisect` which we can use to find the commit where we messed up things.
|
||||
Is there a way to find where we fixed it?”
|
||||
|
||||
For those who don’t know this feature, you have to mark a known “good” and
|
||||
“bad” commit, then git-bisect will go through the commits between this two,
|
||||
present you the corresponding snapshots, and you have to mark each of them
|
||||
as “good” or “bad”. At the end, you will get a commit hash where the bug
|
||||
first occured.
|
||||
|
||||
As it turned out, our developers’ problem rooted in the naming convention
|
||||
of git-bisect: they assumed that the “good” commit must be a working one,
|
||||
while a “bad” one must be the buggy. In this case, we did the following:
|
||||
|
||||
The commit with the customer’s release tag was marked as good (even though
|
||||
this had the bug), and the latest commit on our development branch was
|
||||
marked as “bad” (even though the bug was fixed by then). Now with every
|
||||
snapshot presented by git-bisect we had to do the opposite what you usually
|
||||
do: mark commits still having the bug as “good”, and commits that don’t as
|
||||
“bad”. At the end, we had the hash of the commit that fixed the bug (among
|
||||
some other things; luckily, the developer who pushed that commit had a
|
||||
workflow that introduced a lot of cherry-picking and squashing before the
|
||||
push, so he could easily find the bit that actually fixed the problem in
|
||||
his local repository with the same technique).
|
||||
|
||||
[This StackOverflow answer](http://stackoverflow.com/a/17153598/1305139)
|
||||
suggests the very same, but with some aliases:
|
||||
|
||||
{% gist a98f4aab84659d60364e %}
|
@ -0,0 +1,23 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Good bye, Digital Ocean! Hello again, GitHub!"
|
||||
date: 2015-04-25 21:18:56
|
||||
permalink: /blog/2015/4/25/good-bye-digital-ocean-hello-again-github
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Few years ago I have signed up for a
|
||||
[Digital Ocean](https://www.digitalocean.com/) account. I used one
|
||||
single droplet for my private needs, like hosting my private Git
|
||||
repositories and my blog. However, as I didn’t host anything else there
|
||||
except my blog, I decided to shut it down. From now on, my blog is
|
||||
on [GitHub Pages](https://pages.github.com/), as it provides just
|
||||
everything I need (except automatically converting my resume to
|
||||
PDF. But I can live without that.)
|
||||
|
||||
I’m really sorry, Digital Ocean Guys, your hosting is awesome and I’ll
|
||||
keep recommending you to others, but paying for a droplet for one
|
||||
single blog is overkill.
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Cross browser border-radius SASS mixin with varargs"
|
||||
date: 2015-04-27 22:59:56
|
||||
tags: [css, sass]
|
||||
permalink: /blog/2015/4/28/cross-browser-border-radius-sass-mixin-with-varargs
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Few days ago I needed to create style sheets with many rounded boxes,
|
||||
where different corners had to be rounded differently (think about
|
||||
Bootstrap’s [button
|
||||
groups](http://getbootstrap.com/components/#btn-groups)).
|
||||
|
||||
CSS has this nifty shorthand to specify border width in one line, like
|
||||
with `border-width: 1px 2px 3px 4px`, but it lacks the same for
|
||||
`border-radius`. So I decided to create something similar using [Sass
|
||||
mixins](http://sass-lang.com/guide#topic-6) with dynamic
|
||||
parameters. Another nice feature you get using the `border-width`
|
||||
shorthand is that you can specify less than four parameters, and the
|
||||
values will be applied on different sides of your box, so in the end
|
||||
all side will have the whole `border-width` set.
|
||||
|
||||
I wanted to achieve the same for my `border-radius` mixin, although I
|
||||
could not start specifically with the `top` side. I decided to go with
|
||||
the top right corner for the first parameter, while trying to keep a
|
||||
sane repeating pattern. Here is the result:
|
||||
|
||||
{% gist 313b227434ecc5d85d7b border-radius.sass %}
|
39
_posts/2015-06-07-paramconverter-a-la-django.markdown
Normal file
39
_posts/2015-06-07-paramconverter-a-la-django.markdown
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
layout: post
|
||||
title: "@ParamConverter à la Django"
|
||||
date: 2015-06-07 18:14:32
|
||||
tags: [python, django]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
One thing I really miss from [Django](https://www.djangoproject.com/)
|
||||
is [Symfony](http://symfony.com)’s
|
||||
[@ParamConverter](http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html). It
|
||||
made my life so much easier while developing with Symfony. In Django,
|
||||
of course, there is
|
||||
[get_object_or_404](https://docs.djangoproject.com/en/dev/topics/http/shortcuts/#get-object-or-404),
|
||||
but, for example, in one of my projects I had a view that had to resolve 6(!)
|
||||
objects from the URL, and writing `get_object_or_404` six times is not what a
|
||||
programmer likes to do (yes, this view had a refactor later on). A quick Google
|
||||
search gave me one [usable
|
||||
result](http://openclassrooms.com/forum/sujet/middleware-django-genre-paramconverter-doctrine)
|
||||
(in French), but it was very generalized that I cannot always use. Also, it was
|
||||
using a middleware, which may introduce performance issues
|
||||
sometimes<sup>[citation needed]</sup>. So I decided to go with decorators, and
|
||||
at the end, I came up with this:
|
||||
|
||||
{% gist 498a32297f39b4960ad7 helper.py %}
|
||||
|
||||
Now I can decorate my views, either class or function based, with
|
||||
`@convert_params(User, (Article, 'aid'), (Paragraph, None, 'pid'),
|
||||
(AnotherObject, None, None, 'obj'))` and all the magic happens in the
|
||||
background. The `user_id` parameter passed to my function will be
|
||||
popped off, and be resolved against the `User` model by using the `id`
|
||||
field; the result is put in the new `user` parameter. For Article, the
|
||||
`aid` parameter will be matched against the `id` field of the
|
||||
`Article` model putting the result into `article`, and finally, the
|
||||
`another_object_id` will be matched against the `id` field of the
|
||||
`AnotherObject` model, but in this case, the result is passed to the
|
||||
original function as `obj`.
|
20
_posts/2015-08-19-foss-failmeter.md
Normal file
20
_posts/2015-08-19-foss-failmeter.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
layout: post
|
||||
title: "F/OSS Fail meter"
|
||||
date: 2015-08-19 10:12:19
|
||||
tags: [development]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I have recently bumped into [this
|
||||
article](http://spot.livejournal.com/308370.html). Naturally, I quickly
|
||||
calculated the FAIL metrics for all my projects (most of them are pretty high).
|
||||
To ease calculation, I made up a
|
||||
[small page]({{ '/failmeter/' | prepend: site.baseurl }}) based on this list
|
||||
(although I have divided the points by 5; I really don’t understand why spot is
|
||||
using such big points if all of them can be divided by 5). Feel free to use it,
|
||||
and if you have any recommendations (point additions/removal, new categories,
|
||||
etc.), leave me a comment!
|
228
_posts/2015-08-27-how-my-email-gets-to-that-other-guy.markdown
Normal file
228
_posts/2015-08-27-how-my-email-gets-to-that-other-guy.markdown
Normal file
@ -0,0 +1,228 @@
|
||||
---
|
||||
layout: post
|
||||
title: "How my e-mail gets to that other guy?"
|
||||
date: 2015-08-27 21:47:19
|
||||
tags: [technology]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
A friend of mine asked me how it is possible that she pushes buttons on her
|
||||
keyboard and mouse, and in an instant her peer reads the text she had in her
|
||||
mind. This is a step-by-step introduction of what happens in-between.
|
||||
|
||||
#### From your mind to your computer
|
||||
|
||||
When you decide to write an e-mail to an acquaintance of yours, you open up
|
||||
your mailing software (this document doesn’t cover using mail applications
|
||||
you access through your browsers, just plain old Thunderbird, Outlook or
|
||||
similar programs. However, it gets the same after the mail left your
|
||||
computer), and press the “New Mail” button. What happens during this process
|
||||
is not covered in this article, but feel free to ask me in a comment! Now
|
||||
that you have your Mail User Agent (MUA) up and running, you begin typing.
|
||||
|
||||
When you press a button on your keyboard or mouse, a bunch of bits gets
|
||||
through the wire (or through air, if you went wireless) and get into your
|
||||
computer. I guess you learned about Morse during school; imagine two
|
||||
[Morse operators](http://www.uscupstate.edu/academics/education/aam/lessons/susan_sawyer/morse%20code.jpg),
|
||||
one in your keyboard/mouse, and one in your computer. Whenever you press a
|
||||
key, that tiny creature sends a series of short and long beeps (called 0 or
|
||||
1 bits, respectively) to the operator in your computer (fun fact: have you
|
||||
ever seen someone typing at an amazing speed of 5 key presses per second?
|
||||
Now imagine that whenever that guy presses a key on their keyboard, that
|
||||
tiny little Morse operator pressing his button 16 times for each key press,
|
||||
with perfect timing so that the receiving operator can decide if that was a
|
||||
short or long beep.)
|
||||
|
||||
Now that the code got to the operator inside the machine, it’s up to him to
|
||||
decode it. The funny thing about keyboards and computers is that the
|
||||
computer doesn’t receive the message “Letter Q was pressed”, but instead
|
||||
“The second button on the second row was pressed” (a number called scan
|
||||
code). At this time the operator decodes this information (in this example
|
||||
it is most likely this Morse code: `···-···· -··-····`) and checks one of
|
||||
his tables titled “Current Keyboard Layout.” It says this specific key
|
||||
corresponds to letter ‘Q’, so it forwards this information (I mean the
|
||||
letter; after this step your computer doesn’t care which plastic slab you
|
||||
hit, just the letter ‘Q’) to your MUA, inserts it into the mail in its
|
||||
memory, then displaying it happily (more about this step later).
|
||||
|
||||
When you finish your letter you press the send button of your MUA. First it
|
||||
converts all the pretty letters and pictures to something a computer can
|
||||
understand (yes, those Morse codes, or more precisely, zeros and ones,
|
||||
again). Then it adds loads of meta data, like your name and e-mail address,
|
||||
the current date and time including the time zone and pass it to the sending
|
||||
parts of the MUA so the next step can begin.
|
||||
|
||||
#### IP addresses, DNS and protocols
|
||||
|
||||
The Internet is a huge amount of computers connected with each other, all of
|
||||
them having at least one address called IP address that looks something like
|
||||
this: `123.234.112.221`. These are four numbers between 0 and 255 inclusive,
|
||||
separated by dots. This makes it possible to have 4,294,967,296 computers.
|
||||
With the rules of address assignment added, this is actually reduced to
|
||||
3,702,258,432; a huge number, still, but it is not enough, as in the era of
|
||||
the Internet of Things everything is interconnected, up to and possibly
|
||||
including your toaster. Thus, we are slowly transitioning to a new
|
||||
addressing scheme that looks like this:
|
||||
`1234:5678:90ab:dead:beef:9876:5432:1234`. This gives an enormous amount of
|
||||
340,282,366,920,938,463,463,374,607,431,768,211,456 addresses, with only
|
||||
4,325,185,976,917,036,918,000,125,705,034,137,602 of them being reserved,
|
||||
which gives us only a petty
|
||||
335,957,180,944,021,426,545,374,481,726,734,073,854 available.
|
||||
|
||||
Imagine a large city with
|
||||
[that many buildings](http://www.digitallifeplus.com/wp-content/uploads/2012/07/new-york-city-aerial-5.jpg),
|
||||
all of them having only a number: their IP address. No street names, no
|
||||
company names, no nothing. But people tend to be bad at memorizing numbers,
|
||||
so they started to give these buildings names. For example there is a house
|
||||
with the number `216.58.209.165`, but between each other, people call it
|
||||
`gmail.com`. Much better, isn’t it? Unfortunately, when computers talk, they
|
||||
only understand numbers so we have to provide them just that.
|
||||
|
||||
As remembering this huge number of addresses is a bit inconvenient, we
|
||||
created Domain Name Service, or DNS for short. A “domain name” usually (but
|
||||
not always) consist of two strings of letters, separated by dots (e.g.
|
||||
polonkai.eu, gmail.com, my-very-long-domain.co.uk, etc.), and a hostname is
|
||||
a domain name occasionally prefixed with something (e.g. **www**.gmail.com,
|
||||
**my-server**.my-very-long-domain.co.uk, etc.) One of the main jobs of DNS
|
||||
is to keep record of hostname/address pairs. When you enter `gmail.com`
|
||||
(which happens to be both a domain name and a hostname) in your browser’s
|
||||
address bar, your computer asks the DNS service if it knows the actual
|
||||
address of the building that people call `gmail.com`. If it does, it will
|
||||
happily tell your computer the number of that building.
|
||||
|
||||
Another DNS job is to store some meta data about these domain names. For
|
||||
such meta data there are record types, one of these types being the Mail
|
||||
eXchanger, or MX. This record of a domain tells the world who is handling
|
||||
incoming mails for the specified domain. For `gmail.com` this is
|
||||
`gmail-smtp-in.l.google.com` (among others; there can be multiple records of
|
||||
the same type, in which case they usually have priorities, too.)
|
||||
|
||||
One more rule: when two computers talk to each other they use so called
|
||||
protocols. These protocols define a set of rules on how they should
|
||||
communicate; this includes message formatting, special code words and such.
|
||||
|
||||
#### From your computer to the mail server
|
||||
|
||||
Your MUA has two settings called SMTP server address SMTP port number (see
|
||||
about that later). SMTP stands for Simple Mail Transfer Protocol, and
|
||||
defines the rules on how your MUA, or another mail handling computer should
|
||||
communicate with a mail handling computer when *sending* mail. Most probably
|
||||
your Internet Service Provider gave you an SMTP server name, like
|
||||
`smtp.aol.com` and a port number like `587`.
|
||||
|
||||
When you hit that send button of yours, your computer will check with the
|
||||
DNS service for the address of the SMTP server, which, for `smtp.aol.com`,
|
||||
is `64.12.88.133`. The computer puts this name/address pair into its memory,
|
||||
so it doesn’t have to ask the DNS again (this technique is called caching
|
||||
and is widely used wherever time consuming operations happen).
|
||||
|
||||
Then it will send your message to the given port number of this newly
|
||||
fetched address. If you imagined computers as office buildings, you can
|
||||
imagine port numbers as departments and there can be 65535 of them in one
|
||||
building. The port number of SMTP is usually 25, 465 or 587 depending on
|
||||
many things we don’t cover here. Your MUA prepares your letter, adding your
|
||||
e-mail address and the recipients’, together with other information that may
|
||||
be useful for transferring your mail. It then puts this well formatted
|
||||
message in an envelope and writes “to building `64.12.88.133`, dept. `587`”,
|
||||
and puts it on the wire so it gets there (if the wire is broken, the
|
||||
building does not exist or there is no such department, you will get an
|
||||
error message from your MUA). Your address and the recipient’s address are
|
||||
inside the envelope; other than the MUA, your own computer is not concerned
|
||||
about it.
|
||||
|
||||
The mailing department (or instead lets call it the Mail Transfer Agent,
|
||||
A.K.A. MTA) now opens this envelope and reads the letter. All of it, letter
|
||||
by letter, checking if your MUA formatted it well. More than probably it
|
||||
also runs your message through several filters to decide if you are a bad
|
||||
guy sending some unwanted letter (also known as spam), but most importantly
|
||||
it fetches the recipients address. It is possible, e.g. when you send an
|
||||
e-mail within the same organization, that the recipient’s address is handled
|
||||
by this very same computer. In this case the MTA puts the mail to the
|
||||
recipient’s mailbox and the next step is skipped.
|
||||
|
||||
#### From one server to another
|
||||
|
||||
Naturally, it is possible to send an e-mail from one company to another, so
|
||||
these MTAs don’t just wait for e-mails from you, but also communicate with
|
||||
each other. When you send a letter from your `example@aol.com` address to me
|
||||
at `gergely@polonkai.eu`, this is what happens.
|
||||
|
||||
In this case, the MTA that initially received the e-mail from you (which
|
||||
happened to be your Internet Service Provider’s SMTP server) turns to the
|
||||
DNS again. It will ask for the MX record of the domain name specified by the
|
||||
e-mail address, (the part after the `@` character, in my case,
|
||||
`polonkai.eu`), because the server mentioned there must be contacted, so
|
||||
they can deliver your mail for me. My domain is configured so its primary MX
|
||||
record is `aspmx.l.google.com` and the secondary is
|
||||
`alt1.aspmx.l.google.com` (and 5 more. Google likes to play it safe.) The
|
||||
MTA then gets the first server name, asks the DNS for its address, and tries
|
||||
to send a message to the `173.194.67.27` (the address of
|
||||
`aspmx.l.google.com`), same department. But unlike your MUA, MTAs don’t have
|
||||
a pre-defined port number for other MTAs (although there can be exceptions).
|
||||
Instead, they use well-known port numbers, `465` and `25`. If the MTA on
|
||||
that server cannot be contacted for any reason, it tries the next one on the
|
||||
list of MX records. If none of the servers can be contacted, it will retry
|
||||
based on a set of rules defined by the administrators, which usually means
|
||||
it will retry after 1, 4, 24 and 48 hours. If there is still no answer after
|
||||
that many attempts, you will get an error message back, in the form of an
|
||||
e-mail sent directly by the SMTP server.
|
||||
|
||||
Once the other MTA could be contacted, your message is sent there. The
|
||||
original envelope you used is discarded, and a new one is used with the
|
||||
address and dept. number (port) of the receiving MTA. Also, your message
|
||||
gets altered a little bit, as most MTAs are kind enough (ie. not sneaky) to
|
||||
add a clause to your message stating “the MTA at <organization> has checked
|
||||
and forwarded this message.”
|
||||
|
||||
It is possible, though not likely, that your message gets through more than
|
||||
two MTAs (one at your ISP and one at the receiver’s) before arriving to its
|
||||
destination. At the end, an MTA will say that “OK, this recipient address is
|
||||
handled by me”, your message stops and stays there, put in your peer’s
|
||||
mailbox.
|
||||
|
||||
##### The mailbox
|
||||
|
||||
Now that the MTA has passed your mail to the mailbox team (I call it a team
|
||||
instead of department because the tasks described here are usually handled
|
||||
by the MTA, too), it reads it. (Pesky little guys are these mail handling
|
||||
departments, aren’t they?) If the mailbox has some filtering rules, like “if
|
||||
XY sends me a letter, mark it as important” or “if the letter has a specific
|
||||
word in its subject, put it in the XY folder”, it executes them, but the
|
||||
main point is to land the message in the actual post box of the recipient.
|
||||
|
||||
#### From the post box to the recipients computer
|
||||
|
||||
When the recipient opens their MUA, it will look to a setting usually called
|
||||
“Incoming mail server”. Just like the SMTP server, it has a name and port
|
||||
number, along with a server type. This type can vary from provider to
|
||||
provider, and is usually one of POP3 (pretty old protocol, doesn’t even
|
||||
support folders on its own), IMAP (a newer one, with folders and message
|
||||
flags like “important”), MAPI (a dialect of IMAP, created by Microsoft as
|
||||
far as I know), or plain old mbox files on the receiving computer (this last
|
||||
option is pretty rare nowadays, so I don’t cover this option. Also, if you
|
||||
use these, you most probably don’t really need this article to understand
|
||||
how these things work.) This latter setting defines the protocol, telling
|
||||
your MUA how to “speak” to the post box.
|
||||
|
||||
So your MUA turns to the DNS once more to get the address of your incoming
|
||||
mail server and contacts it, using the protocol set by the server type. At
|
||||
the end, the recipients computer will receive a bunch of envelopes including
|
||||
the one that contains your message. The MUA opens them one by one and reads
|
||||
them, making a list ordered by their sender or subject, or the date of
|
||||
sending.
|
||||
|
||||
#### From the recipient’s comupter to their eyes
|
||||
|
||||
When the recipient then clicks on one of these mails, the MUA will fetch all
|
||||
the relevant bits like the sender, the subject line, the date of sending and
|
||||
the contents itself and sends it to the “printing” department (I use quotes
|
||||
as they don’t really print your mail on paper, they just convert it to a
|
||||
nice image so the recipient can see it. This is sometimes referred to as a
|
||||
rendering engine). Based on a bunch of rules they pretty-print it and send
|
||||
it to your display as a new series of Morse codes. Your display then decides
|
||||
how it will present it to the user: draw the pretty pictures if it is a
|
||||
computer screen, or just raise and lower some hard dots that represents
|
||||
letters on a Braille terminal.
|
@ -0,0 +1,53 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Emacs: Implement a GObject’s virtual function"
|
||||
date: 2016-01-13 13:31:12
|
||||
tags: [c, development, emacs]
|
||||
published: true
|
||||
author:
|
||||
name: "Gergely Polonkai"
|
||||
email: "gergely@polonkai.eu"
|
||||
---
|
||||
|
||||
I have recently started creating a GLib implementation of the
|
||||
Matrix.org API. For that, I have created a GObject interface,
|
||||
MatrixAPI, which has as many virtual functions as API calls (which is
|
||||
a lot, and expanding). This way I ended up with the following scenario.
|
||||
|
||||
In `matrix-api.h` I had a struct like this, with a lot more elements:
|
||||
|
||||
typedef struct {
|
||||
void (*initial_sync)(MatrixAPI *api,
|
||||
MatrixAPICallback callback,
|
||||
gpointer user_data,
|
||||
GError **error);
|
||||
void (*sync)(MatrixAPI *api,
|
||||
MatrixAPICallback callback,
|
||||
gpointer user_data,
|
||||
GError **error);
|
||||
…
|
||||
|
||||
And in `matrix-http-api.c`, which implements `MatrixAPI`, I have a
|
||||
function like this (again, with a lot more elements):
|
||||
|
||||
static void
|
||||
matrix_http_api_matrix_api_init(GObjectInterface *iface)
|
||||
{
|
||||
iface->initial_sync = i_initial_sync;
|
||||
iface->sync = i_sync;
|
||||
…
|
||||
}
|
||||
|
||||
And every time I wanted to implement a new function from the vtable, I
|
||||
had to copy the prototype, and add an `iface->foo_bar = i_foo_bar`
|
||||
line and an actual function header for `i_foo_bar` with the same
|
||||
parameters. That’s a cumbersome job for more than 40 function
|
||||
headers. But emacs comes to the rescue!
|
||||
|
||||
{% gist bfd36be8b515edced3d2 implement-gobject-vfunc.el %}
|
||||
|
||||
Now all I have to do is to copy the whole vtable entry into
|
||||
`matrix_http_api_matrix_api_init()`, execute `M-x
|
||||
implement-gobject-vfunc`, then put the same vtable entry somewhere
|
||||
before the interface init function, and execute `M-x
|
||||
implement-gobject-vfunc-prototype`.
|
33
_posts/2016-02-26-vala-interface-madness.md
Normal file
33
_posts/2016-02-26-vala-interface-madness.md
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Vala interface madness"
|
||||
date: 2016-02-26 13:07:52
|
||||
tags: [vala, development]
|
||||
published: true
|
||||
author:
|
||||
name: "Gergely Polonkai"
|
||||
email: "gergely@polonkai.eu"
|
||||
---
|
||||
|
||||
Although I have just started making it in C, I decided to move my
|
||||
Matrix GLib SDK to Vala. First to learn a new language, and second
|
||||
because it is much easier to write GObject based stuff with it.
|
||||
|
||||
For the first step I created a `.vapi` file from my existing sources,
|
||||
so the whole SDK prototype was available for me in Vala.
|
||||
|
||||
I had a `MatrixEvent` class that implemented the `GInitable`
|
||||
interface, and many others were subclassed `MatrixEvent`. For some
|
||||
reason I don’t remember, I created the following header for one of the
|
||||
event classes:
|
||||
|
||||
public class MatrixPresenceEvent : GLib.Object, GLib.Initable {
|
||||
|
||||
This is nice and everything, but as I didn’t create an `init()` method
|
||||
for `MatrixPresenceEvent`, it tried to use the one from the parent
|
||||
class and somehow got into an infinite loop. The Vala transformer
|
||||
(`valac`), however, doesn’t mention this.
|
||||
|
||||
Lessons learned: if you implement an interface on a subclass that is
|
||||
implemented by the parent don’t forget to add the necessary functions
|
||||
to the subclass.
|
@ -0,0 +1,46 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Emacs package to generate GObject boilerplate"
|
||||
date: 2016-09-28 15:40:15
|
||||
tags: [gnome, development]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Before I started using Vala (and sometimes even after that) I often
|
||||
needed to generate new classes based
|
||||
on [GObject](https://developer.gnome.org/gobject/stable/).
|
||||
|
||||
If you have ever worked with GObject in C, you know how tedious it can
|
||||
be. You need a pretty long boilerplate just to register your class,
|
||||
and, if you want to be introspectable (and readable, actually), your
|
||||
function names can grow really long.
|
||||
|
||||
To overcome this problem back in my ViM days, I used template files,
|
||||
where I could replace class prefixes and names with a few keyboard
|
||||
macros. As I never really dug into ViM scripting other than using some
|
||||
plugins, I never got farther than
|
||||
that. [Then came Emacs]({% post_url 2014-09-17-nyanmacs %}).
|
||||
|
||||
I use Emacs for about two years now very extensively, up to and
|
||||
including GLib-based development. I tried the template approach, but
|
||||
it felt to be a really poor experience, especially given that I made
|
||||
my feet wet with Emacs Lisp. So I dug deeper, and created a package
|
||||
for that.
|
||||
|
||||

|
||||
|
||||
GobGen has its own buffer with some widgets, a bit similar to
|
||||
`customize`. You can enter the name of your new object and its parent,
|
||||
specify some settings. Then you press Generate, and you are presented
|
||||
with two new buffers, one for the `.c` and another for the `.h`
|
||||
boilerplate.
|
||||
|
||||
There are a lot of things to do, actually. There is already an open
|
||||
issue for creating a major mode for this buffer, and there are some
|
||||
minor switches I’d like to add, but it is already usable. You can grab
|
||||
it from [MELPA](https://melpa.org/#/gobgen) (my first package there;
|
||||
woo!) or from
|
||||
my [GitHub account](https://github.com/gergelypolonkai/gobgen.el).
|
65
_posts/2016-10-04-git-merge-stages.md
Normal file
65
_posts/2016-10-04-git-merge-stages.md
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
layout: post
|
||||
title: "git-merge stages"
|
||||
date: 2016-10-04 12:46:00
|
||||
tags: [git]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
This was a mail to my company’s internal Git mailing list, after I
|
||||
realised many colleagues can’t wrap their heads around merge
|
||||
conflicts.
|
||||
|
||||
>Hello all,
|
||||
>
|
||||
>I just saw this on
|
||||
>the [git-users](https://groups.google.com/forum/#!forum/git-users)
|
||||
>list and thought it could help you when you bump into a merge
|
||||
>conflict. It is an excerpt from a mail by Konstantin Khomoutov (one
|
||||
>of the main contributors on the list), with a few modifications of
|
||||
>mine. Happy debugging :)
|
||||
>
|
||||
>>When a merge conflict is detected for a file, Git:
|
||||
>>
|
||||
>>1. Updates the entry for that file in the index to make it contain
|
||||
>> several so-called “stages”:
|
||||
>> * `0`: “Ours” version – that one which was there in this index entry
|
||||
>> before we begun to merge. At the beginning of the conflict, like
|
||||
>> right after the `git merge` or `git rebase` command this won’t
|
||||
>> exist (unless you had the file in the index, which you didn’t, did
|
||||
>> you?). When you resolve the conflict and use `git add
|
||||
>> my/conflicting/file.cc`, this will be the version added to the
|
||||
>> staging area (index), thus, the resolution of the conflict.
|
||||
>> * `1`: The version from the common ancestor commit, ie. the version
|
||||
>> of the file both of you modified.
|
||||
>> * `2`: The version from `HEAD`. During a merge, this is the current
|
||||
>> branch. During a rebase, this is the branch or commit you are
|
||||
>> rebasing onto, which usually will be `origin/develop`).
|
||||
>> * `3`: The version being merged, or the commit you are rebasing.
|
||||
>>2. Updates the file in the work tree to contain conflict markers and
|
||||
>> the conflicting chunks of text between them (and the text from the
|
||||
>> common ancestor if the `diff3` style of conflict markers was set).
|
||||
>>
|
||||
>>Now you can use the numbers in point 1 to access the different stages
|
||||
>>of the conflicting file. For example, to see the common ancestor (the
|
||||
>>version both of you modified), use
|
||||
>>
|
||||
>>```
|
||||
>>git show :1:my/conflicting/file.cc
|
||||
>>```
|
||||
>>
|
||||
>>Or, to see the difference between the two conflicting versions, try
|
||||
>>
|
||||
>>```
|
||||
>>git diff :2:my/conflicting/file.cc :3:my/conflicting/file.cc
|
||||
>>```
|
||||
>>
|
||||
>>**Note** that you can’t use the `:0:` stage *before* you stage your
|
||||
>>resolution with `git add`, and you can’t use the `:2:` and `:3:`
|
||||
>>stages *after* you staged the resolution.
|
||||
>>
|
||||
>>Fun fact: behind the scenes, these are the files (*revisions*) `git mergetool`
|
||||
>>accesses when it presents you the conflict visually.
|
98
_posts/2016-11-03-how-i-started-with-emacs.md
Normal file
98
_posts/2016-11-03-how-i-started-with-emacs.md
Normal file
@ -0,0 +1,98 @@
|
||||
---
|
||||
layout: post
|
||||
title: "How I started with Emacs"
|
||||
date: 2016-11-03 09:58:41
|
||||
tags: [emacs]
|
||||
published: true
|
||||
authon:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Sacha Chua has a nice
|
||||
[Emacs chat intro](http://sachachua.com/blog/2013/04/emacs-chat-intro/) article
|
||||
back from 2013. I write this post half because she asks there about my
|
||||
(OK, anyone’s) first Emacs moments, and half because I plan to do it
|
||||
for months now.
|
||||
|
||||
I wanted to start using Emacs 6(ish) years ago, and I was like
|
||||
“<kbd>C-x</kbd> what”? (Note that back around 1998, I was among the
|
||||
people who exited `vi` by killing it from another terminal after a
|
||||
bunch of tries & fails like
|
||||
[these](http://osxdaily.com/2014/06/12/how-to-quit-vim/).)
|
||||
|
||||
I tried to come back to Emacs a lot of times. And I mean a *lot*,
|
||||
about every two months. I suddenly learned what these cryptic key
|
||||
chord descriptions mean (`C` is for <kbd>Control</kbd> and `M` is for
|
||||
<kbd>Meta</kbd>, which is actually <kbd>Alt</kbd>), but somehow it
|
||||
didn’t *click*. I remained a ViM power user with a huge pile of
|
||||
3<sup>rd</sup> party plugins.
|
||||
Then [I found Nyan-macs]({% post_url 2014-09-17-nyanmacs %}),
|
||||
which converted me to Emacs, and it is final now. Many of my friends
|
||||
thought I’m just kidding this being the cause, but I’m not. I’m a huge
|
||||
fan of Nyan cat (did you know there is even a site
|
||||
called [nyan.cat](http://nyan.cat/)?) and since then I have it in my
|
||||
mode line:
|
||||
|
||||

|
||||
|
||||
…in my `eshell` prompt:
|
||||
|
||||

|
||||
|
||||
…and I also [zone out](https://www.emacswiki.org/emacs/ZoneMode) with
|
||||
Nyan cat:
|
||||
|
||||

|
||||
|
||||
Now on to more serious stuff. After browsing through all the packages
|
||||
provided by [ELPA](http://elpa.gnu.org/), I found tons of useful (and
|
||||
sometimes, less useful) packages,
|
||||
like
|
||||
[Helm](https://github.com/emacs-helm/helm/wiki),
|
||||
[company](http://company-mode.github.io/),
|
||||
[gtags](https://www.emacswiki.org/emacs/GnuGlobal) (which introduced
|
||||
me to GNU Global, removing Exuberant ctags from my
|
||||
life),
|
||||
[magit](https://magit.vc/),
|
||||
[Projectile](http://batsov.com/projectile/),
|
||||
and [Org](http://orgmode.org/) (OK, it’s actually part of Emacs for a
|
||||
while, but still). I still use these few, but in a month or two, I
|
||||
started
|
||||
to [version control](https://github.com/gergelypolonkai/my-emacs-d) my
|
||||
`.emacs.d` directory, so I can easily transfer it between my home and
|
||||
work machine (and for a few weeks now, even to my phone: I’m using
|
||||
Termux on Android). Then, over these two years I wrote some packages
|
||||
like [GobGen](https://github.com/gergelypolonkai/gobgen.el), and a
|
||||
small addon for Calendar
|
||||
providing
|
||||
[Hungarian holidays](https://github.com/gergelypolonkai/hungarian-holidays),
|
||||
and I found a lot more (in no particular
|
||||
order):
|
||||
[git-gutter](https://github.com/syohex/emacs-git-gutter),
|
||||
[multiple-cursors](https://github.com/magnars/multiple-cursors.el),
|
||||
[origami](https://github.com/gregsexton/origami.el),
|
||||
[ace-window](https://github.com/abo-abo/ace-window),
|
||||
[avy](https://github.com/abo-abo/avy),
|
||||
[beacon](https://github.com/Malabarba/beacon), and a lot more.
|
||||
|
||||
What is more important (to me) is that I started using
|
||||
the [use-package](https://github.com/jwiegley/use-package) package,
|
||||
which can automatically download packages that are not installed on my
|
||||
current local system. Together
|
||||
with
|
||||
[auto-package-update](https://github.com/rranelli/auto-package-update.el),
|
||||
it is *very* practical.
|
||||
|
||||
In addition, I started to follow the blogs of a bunch of Emacs
|
||||
users/gurus. I’ve already
|
||||
mentioned [Sacha Chua](http://sachachua.com/). She’s a charming,
|
||||
cheerful person, writing a lot about Emacs and project management
|
||||
(among other things). Another one
|
||||
is [Bozhidar Batsov](http://batsov.com/), who, among other things, had
|
||||
an initiate to lay down the foundation of
|
||||
a
|
||||
[common Elisp coding style](https://github.com/bbatsov/emacs-lisp-style-guide). Another
|
||||
favourite of mine
|
||||
is [Endless Parentheses](http://endlessparentheses.com/), whence I got
|
||||
a lot of ideas.
|
27
_posts/2016-11-10-edit-file-as-other-user-in-emacs.md
Normal file
27
_posts/2016-11-10-edit-file-as-other-user-in-emacs.md
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Edit file as another user in Emacs"
|
||||
date: 2016-11-10 08:57:12
|
||||
tags: [development, emacs]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I have recently found
|
||||
[this article](http://emacsredux.com/blog/2013/04/21/edit-files-as-root/) by
|
||||
Bozhidar Batsov on opening the current file as root. I barely use
|
||||
[tramp](https://www.gnu.org/software/tramp/) for sudo access, but when I do,
|
||||
I almost never use root as the target user. So I decided to fix it for my
|
||||
needs.
|
||||
|
||||
{% gist 192c83aa0556d5cdaf4018f57b75a84b %}
|
||||
|
||||
If the user is not specified, the default is still root. Also, if the
|
||||
current buffer is not visiting a file, I prompt for a filename. As I’m not
|
||||
an `ido` user, I didn’t bother calling
|
||||
`ido-read-file-name`; [`helm`](https://github.com/emacs-helm/helm/wiki)
|
||||
overrides `read-file-name` for me anyway.
|
||||
|
||||
Unlike Bozhidar, I barely use this feature, so I didn’t bind this to a key.
|
28
_posts/2016-11-18-get-passwords-from-id-manager.md
Normal file
28
_posts/2016-11-18-get-passwords-from-id-manager.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Get account data programatically from id-manager"
|
||||
date: 2016-11-18 12:43:13
|
||||
tags: [emacs]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I recently started
|
||||
using [`id-manager`](https://github.com/kiwanami/emacs-id-manager). It is a
|
||||
nice little package that can store your passwords, encrypting them with
|
||||
GPG. My original reason was to store my GitHub access token
|
||||
for [`github-notifier`](https://github.com/xuchunyang/github-notifier.el),
|
||||
but it soon turned out, it’s not *that* easy.
|
||||
|
||||
`id-manager` is a nice package when it comes to storing your password, and
|
||||
retrieving them for your own eyes. But it cannot retrieve account data
|
||||
programatically. Taking a look into its source code, I came up with this
|
||||
solution:
|
||||
|
||||
{% gist 8bad70502ac563864080f754fce726c3 idm.el %}
|
||||
|
||||
I currently need only the account ID (ie. the username) and the password,
|
||||
but it’s pretty easy to add a macro to get the `memo` or `update-time`
|
||||
fields, too.
|
@ -0,0 +1,47 @@
|
||||
---
|
||||
layout: "post"
|
||||
title: "Add Python docstring to the beginning of anything in Emacs"
|
||||
date: 2016-11-30 07:52:37
|
||||
tags: [development, python, emacs]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Now that I write Python code for a living, I write a lot of functions,
|
||||
classes, and modules. What I still tend to forget, and also find tedious,
|
||||
is adding docstrings. Unlike many developers, writing documentation is not
|
||||
an enemy of mine, but it usually comes to my mind when I finish
|
||||
implementation. The procedure, roughly, is this:
|
||||
|
||||
* Decide where I am (in a function, in a class but not in one of its
|
||||
methods, or not inside such a block at all)
|
||||
* Go to the beginning of the thing
|
||||
* Insert `"""`
|
||||
* Leave a blank line
|
||||
* Insert `"""`
|
||||
|
||||
One of my mottos is if something takes more than one step and you have to do
|
||||
it more than twice, you should automate it after the first time. This puts
|
||||
a small(ish) overhead on the second invocation (when you implement the
|
||||
automation), but it usually worth the time.
|
||||
|
||||
Since I use Emacs for pretty much everything coding-related (and many more,
|
||||
but that’s the topic of a different post), I wrote a small function to do it
|
||||
for me.
|
||||
|
||||
{% gist 7b062a00d3b8a2555024521273cecfee python-docstring.el %}
|
||||
|
||||
There are still a lot of things to improve:
|
||||
|
||||
* it always inserts double quotes (althoug I couldn’t show a use-case when
|
||||
single quotes are preferred)
|
||||
* it doesn’t check for an existing docstring, just happily inserts a new one
|
||||
(leaving the old one alone, but generating a syntax error this way)
|
||||
* it would also be nice if I could jump to the beginning of a file even from
|
||||
a class method. I guess I will use prefix keys for that, but I’m not sure
|
||||
yet.
|
||||
|
||||
You can bet I will implement these features, so check back soon for an
|
||||
updated version!
|
20
_posts/2016-12-08-slugify-in-python3.md
Normal file
20
_posts/2016-12-08-slugify-in-python3.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Slugify in Python 3"
|
||||
date: 2016-12-08 12:54:19
|
||||
tags: [development, python]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
Today I needed a function to create a slug (an ASCII-only representation of
|
||||
a string). I went Googling a bit, and found an
|
||||
excellend [Flask snippet](http://flask.pocoo.org/snippets/5/). Problem is,
|
||||
it is designed for Python 2, so I came up with a Python 3 version.
|
||||
|
||||
{% gist 1866fd363f75f4da5f86103952e387f6 slugify.py %}
|
||||
|
||||
As I don’t really like the transliteration done in the first example
|
||||
(e.g. converting ü to ue), I went with the second example.
|
@ -0,0 +1,115 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Finding non-translated strings in Python code"
|
||||
date: 2016-12-22 09:35:11
|
||||
tags: [development, python]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
When creating multilingual software, be it on the web, mobile, or desktop,
|
||||
you will eventually fail to mark strings as translatable. I know, I know,
|
||||
we developers are superhuman and never do that, but somehow I stopped
|
||||
trusting myself recently, so I came up with an idea.
|
||||
|
||||
Right now I assist in the creation of a multilingual site/web application,
|
||||
where a small part of the strings come from the Python code instead of HTML
|
||||
templates. Call it bad practice if you like, but I could not find a better
|
||||
way yet.
|
||||
|
||||
As a start, I tried to parse the source files with simple regular
|
||||
expressions, so I could find anything between quotation marks or
|
||||
apostrophes. This attempt quickly failed with strings that had such
|
||||
characters inside, escaped or not; my regexps became so complex I lost all
|
||||
hope. Then the magic word “lexer” came to mind.
|
||||
|
||||
While searching for ready made Python lexers, I bumped into the awesome
|
||||
`ast` module. AST stands for Abstract Syntax Tree, and this module does
|
||||
that: parses a Python file and returns a tree of nodes. For walking through
|
||||
these nodes there is a `NodeVisitor` class (among other means), which is
|
||||
meant to be subclassed. You add a bunch of `visitN` methods (where `N` is
|
||||
an `ast` class name like `Str` or `Call`), instantiate it, and call its
|
||||
`visit()` method with the root node. For example, the `visitStr()` method
|
||||
will be invoked for every string it finds.
|
||||
|
||||
#### How does it work?
|
||||
|
||||
Before getting into the details, let’s me present you the code I made:
|
||||
|
||||
{% gist 1a16a47e5a1971ca33e58bdfd88c5059 string-checker.py %}
|
||||
|
||||
The class initialization does two things: creates an empty `in_call` list
|
||||
(this will hold our primitive backtrace), and saves the filename, if
|
||||
provided.
|
||||
|
||||
`visitCall`, again, has two tasks. First, it checks if we are inside a
|
||||
translation function. If so, it reports the fact that we are translating
|
||||
something that is not a raw string. Although it is not necessarily a bad
|
||||
thing, I consider it bad practice as it may result in undefined behaviour.
|
||||
|
||||
Its second task is to walk through the positional and keyword arguments of
|
||||
the function call. For each argument it calls the `visit_with_trace()`
|
||||
method.
|
||||
|
||||
This method updates the `in_call` property with the current function name
|
||||
and the position of the call. This latter is needed because `ast` doesn’t
|
||||
store position information for every node (operators are a notable example).
|
||||
Then it simply visits the argument node, which is needed because
|
||||
`NodeVisitor.visit()` is not recursive. When the visit is done (which, with
|
||||
really deeply nested calls like `visit(this(call(iff(you(dare)))))` will be
|
||||
recursive), the current function name is removed from `in_call`, so
|
||||
subsequent calls on the same level see the same “backtrace”.
|
||||
|
||||
The `generic_visit()` method is called for every node that doesn’t have a
|
||||
named visitor (like `visitCall` or `visitStr`. For the same reason we
|
||||
generate a warning in `visitCall`, we do the same here. If there is
|
||||
anything but a raw string inside a translation function call, developers
|
||||
should know about it.
|
||||
|
||||
The last and I think the most important method is `visitStr`. All it does
|
||||
is checking the last element of the `in_call` list, and generates a warning
|
||||
if a raw string is found somewhere that is not inside a translation function
|
||||
call.
|
||||
|
||||
For accurate reports, there is a `get_func_name()` function that takes an
|
||||
`ast` node as an argument. As function call can be anything from actual
|
||||
functions to object methods, this goes all down the node’s properties, and
|
||||
recursively reconstructs the name of the actual function.
|
||||
|
||||
Finally, there are some test functions in this code. `tst` and
|
||||
`actual_tests` are there so if I run a self-check on this script, it will
|
||||
find these strings and report all the untranslated strings and all the
|
||||
potential problems like the string concatenation.
|
||||
|
||||
#### Drawbacks
|
||||
|
||||
There are several drawbacks here. First, translation function names are
|
||||
built in, to the `TRANSLATION_FUNCTIONS` property of the `ShowString` class.
|
||||
You must change this if you use other translation functions like
|
||||
`dngettext`, or if you use a translation library other than `gettext`.
|
||||
|
||||
Second, it cannot ignore untranslated strings right now. It would be great
|
||||
if a pragma like `flake8`’s `# noqa` or `coverage.py`’s `# pragma: no cover`
|
||||
could be added. However, `ast` doesn’t parse comment blocks, so this proves
|
||||
to be challenging.
|
||||
|
||||
Third, it reports docstrings as untranslated. Clearly, this is wrong, as
|
||||
docstrings generally don’t have to be translated. Ignoring them, again, is
|
||||
a nice challenge I couldn’t yet overcome.
|
||||
|
||||
The `get_func_name()` helper is everything but done. As long as I cannot
|
||||
remove that final `else` clause, there may be error reports. If that
|
||||
happens, the reported class should be treated in a new `elif` branch.
|
||||
|
||||
Finally (and the most easily fixed), the warnings are simply printed on the
|
||||
console. It is nice, but it should be optional; the problems identified
|
||||
should be stored so the caller can obtain it as an array.
|
||||
|
||||
#### Bottom line
|
||||
|
||||
Finding strings in Python sources is not as hard as I imagined. It was fun
|
||||
to learn using the `ast` module, and it does a great job. Once I can
|
||||
overcome the drawbacks above, this script will be a fantastic piece of code
|
||||
that can assist me in my future tasks.
|
@ -0,0 +1,34 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Rename automatically named foreign keys with Alembic"
|
||||
date: 2017-01-02 09:41:23
|
||||
tags: [mysql, development, flask, python]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I have recently messed up my Alembic migrations while modifying my
|
||||
SQLAlchemy models. To start with, I didn’t update the auto-generated
|
||||
migration files to name the indexes/foreign keys a name, so Alembic used its
|
||||
own naming scheme. This is not an actual problem until you have to modify
|
||||
columns that have such constraints. I have since fixed this problem, but
|
||||
first I had to find which column references what (I had no indexes other
|
||||
than primary key back then, so I could go with foreign keys only). Here is
|
||||
a query I put together, mostly using
|
||||
[this article](http://www.binarytides.com/list-foreign-keys-in-mysql/).
|
||||
|
||||
``` sql
|
||||
SELECT constraint_name,
|
||||
CONCAT(table_name, '.', column_name) AS 'foreign key',
|
||||
CONCAT(referenced_table_name, '.', referenced_column_name) AS 'references'
|
||||
FROM information_schema.key_column_usage
|
||||
WHERE referenced_table_name IS NOT NULL AND
|
||||
table_schema = 'my_app';
|
||||
```
|
||||
|
||||
Now I could easily drop such constraints using
|
||||
`alembic.op.drop_constraint('users_ibfk1', 'users', type_='foreignkey')` and
|
||||
recreate them with `alembic.op.create_foreign_key('fk_user_client', 'users',
|
||||
'clients', ['client_id'], ['id'])`
|
91
_posts/2017-03-26-category-based-logging-with-flask.md
Normal file
91
_posts/2017-03-26-category-based-logging-with-flask.md
Normal file
@ -0,0 +1,91 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Category-based logging with Flask"
|
||||
date: 2017-03-26 22:00:52
|
||||
tags: [development, python, flask]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I’m in a team who are developing a Flask-based web application, which uses
|
||||
logging extensively. For a while now it spews out a lot of lines so the
|
||||
need arose to index them in ElasticSearch, and more importantly, to search
|
||||
through them for auditing purposes. This latter user story brought up one
|
||||
more question: why don’t we categorize our log messages? I quickly came up
|
||||
with an extended log format (`[auth]` is the new category name):
|
||||
|
||||
[2017-01-14 00:55:42,554] [8286] [INFO] [auth] invalid password for john@example.com [at __init__.py:12, in function utils.validate_login]
|
||||
|
||||
Here, `[auth]` is the category name. In the ideal solution, all I’d have to
|
||||
do is adding `%(category)s` to my formatter, and I could call
|
||||
`app.logger.info('auth', 'invalid password')` to achieve this output.
|
||||
Unfortunately, `Flask.logger` (and, in the background, the `logging` module)
|
||||
is not that easy to tame.
|
||||
|
||||
As it turns out, a Flask application’s `logger` property is a subclass of
|
||||
`logging.Logger`, so my first idea was to monkey patch that class. When the
|
||||
app’s logger is initialised, it subclasses `logging.Logger` and tweaks the
|
||||
log level so it goes down to `logging.DEBUG` if the app is running in debug
|
||||
mode. This is done by using a different logger class depending on the app
|
||||
config. Fortunately it doesn’t directly subclass `logging.Logger`; it calls
|
||||
`logging.getLoggerClass()` to find which class it should extend. To achieve
|
||||
my goals, all I had to do is to subclass the original logger class, and pass
|
||||
it to `logging.setLoggerClass()` *before* I initialise my app, and I have a
|
||||
fail-safe(ish) solution. So far so good, on to the extra category
|
||||
parameter.
|
||||
|
||||
Now if you add a new variable to the formatter like my new `%(category)s`,
|
||||
you get a nifty `KeyError` saying there is no `category` in the format
|
||||
expansion dictionary. If you add `category='auth` to the
|
||||
`app.logger.info()` calls and its cousins, it’s fine, because these methods
|
||||
use the magic `**kwarg` argument to swallow it. Everything goes well until
|
||||
control arrives to the `_log()` method: it complains about that extra
|
||||
`category` keyword argument. Taking a peek at Python’s internals, I found
|
||||
two things: `info()`, `error()`, and co. pass `*args` and `**kwargs` to
|
||||
`_log()` unmodified, and the `_log()` method doesn’t have `**kwargs`
|
||||
present in its definition to swallow it. A little doc reading later I found
|
||||
that if I want to pass extra arguments for such a formatter, I should do it
|
||||
via the `extra` keyword argument to `_log()`. A call like
|
||||
`app.logger.info('invalid password', extra={'category': 'auth'})` solved the
|
||||
problem. Now *that* is tedious.
|
||||
|
||||
My first idea was to override all the standard logging methods like `info()`
|
||||
and `error()`, and handle `category` there. But this resulted in lots of
|
||||
repeating code. I changed the specification a bit, so my calls would look
|
||||
like `info('message', category='auth)` instead of the original plan of
|
||||
`info('auth', 'message')`: as the logging methods pass all keyword arguments
|
||||
to `_log()`, I can handle it there. So at the end, my new logger class
|
||||
only patches `_log()`, by picking out `category` from the kwarg list, and
|
||||
inserting it to `extra` before calling `super`.
|
||||
|
||||
As you can see, this is a bit ugly solution. It requires me, the app
|
||||
author, to know about Flask’s internals (that I can set my own logging class
|
||||
before the app is created, and so the app will use it.) This means if Flask
|
||||
developers change the way how logging is done, I have to adapt and find a
|
||||
workaround for the new version (well, unless they let me directly set the
|
||||
logging class. That would make it easy.)
|
||||
|
||||
What is worse, I must know about Python internals. I know the `extra` kwarg
|
||||
is documented well (I just failed to notice), but this made adding a new
|
||||
formatter variable so hard. Python version doesn’t change as often as Flask
|
||||
version in this project, and I think the standard library won’t really
|
||||
change until 4.0, so I don’t think my tampering with a “protected” method
|
||||
will cause any trouble in the future. Still, this makes me feel a bit
|
||||
uneasy.
|
||||
|
||||
All the above can be circumvented if this class, and the whole solution have
|
||||
some tests. As my class uses the same method as Flask (use
|
||||
`logging.getLoggerClass()` as a base class instead of using
|
||||
`logging.Logger()` directly), if the base logger class changes in Python or
|
||||
in the running environment, my app won’t care. By checking if the app
|
||||
logger can use my special `category` variable (ie. it doesn’t raise an
|
||||
exception *and* the category actually gets into the log output), I made sure
|
||||
my class is used as a base in Flask, so if they change the way they
|
||||
construct `app.logger`, I will know about it when I first run my tests after
|
||||
upgrading Flask.
|
||||
|
||||
If you are interested in such functionality (and more), you can grab it
|
||||
from [GitHub](https://github.com/gergelypolonkai/flask-logging-extras), or
|
||||
via [PyPI](https://pypi.python.org/pypi/Flask-Logging-Extras/).
|
161
_posts/2017-04-25-3-things-school-taught-you.md
Normal file
161
_posts/2017-04-25-3-things-school-taught-you.md
Normal file
@ -0,0 +1,161 @@
|
||||
This is a translation of Mark Manson’s article from
|
||||
2013,
|
||||
[3 things school taught you without you even realizing it](https://markmanson.net/school)
|
||||
|
||||
Középiskolás voltam; 16 éves, és dühös. Az angol tanárom kiadta a
|
||||
feladatot: írjunk bármirõl a középiskolával kapcsolatban. Bármirõl.
|
||||
|
||||
Végül egy középiskolai lövöldözésrõl írtam.
|
||||
|
||||
Annyi csavar volt a történetben, hogy mikor a támadót sarokba szorította a rendõrség, ahelyett, hogy szétloccsantotta volna a saját fejét, tanítani kezdte a diákokat. Aki rosszul viselkedett vagy nem követte az utasításokat, azokat kivégezte. Elsõ ránézésre ezek a kivégzések irracionálisnak és kegyetlennek tûntek, de ahogy a gyerekek idõsebbek lettek, egész pragmatikussá váltak, azzal a céllal, hogy a túlélõket felkészítse a “való életre”. A történet végén, a ballagáson, a támadó könnyek közt ölelte meg a tanítványait, gratulált nekik, és elmondta, milyen büszke az eredményeikre.
|
||||
|
||||
A történet borzasztó értékelést kapott, mint szinte minden fogalmazásom. A jritika mindig ugyanaz volt: túlságosan eltértem a megadott témától, az írásom túl személyes lett, néha offenzív, vagy egyszerûen fura.
|
||||
|
||||
Az iskola meggyõzött arról, hogy rossz író vagyok. Ami érdekes, mivel ma profi író vagyok. Teljes munkaidõben; ebbõl élek. Ehhez mit szól, Mr. Jacobs? A dolog iróniája, hogy az emberek épp azért olvassák az írásaimat, ami miatt a rossz osztályzatokat kaptam: eltérek a megszokott témáktól; extrém személyes vagyok, és sokat megosztok magamból; a történeteim néha offenzívek, vagy egyszerûen furák.
|
||||
|
||||
Rengeteg ember kritizálja, mit és hogyan tanít az oktatási rendszerünk. Nem látok rá különösebb okot, hogy ebbe mélyebben elmerüljek. Nem vagyok sem szakértõ, sem tanár. Egyerûen csak bugyuta dolgokat írok az interneten, hogy az emberek lájkoljanak a Facebookon.
|
||||
|
||||
Vannak azonban gondolataim arról, hogyan mûködik az oktatás; nem mint a tanulás, inkább a szociális/érzelmi fejlõdés színtere.
|
||||
|
||||
Az elmúlt két évben végzett kutatásaim során rengeteg idõt töltöttem annak kutatásával, hogyan is definiáljuk önmagunkah, és mit jelent mindez a boldogságunkra nézve. Miért válnak egyes emberek érzelmileg stabillá, míg mások nem? Miért képesek egyesek függetlennek és felelõsségteljessé válni, míg mások erre képtelenek? Miért készítenek egyesekktopless szelfiket, míg mások nem?
|
||||
|
||||
(Ezen az utolsón még dolgozom.)
|
||||
|
||||
Ahogy beástam magam a jegyzeteimbe, és egyre tisztábban láttam, milyen hatások egészségesek vagy épp egészségtelenek egy felnövõ gyermek számára, egyre gyakrabban gondoltam a középiskolai fogalmazás feladatokra.
|
||||
|
||||
A gyermekkor és a serdülõkor az az idõszak, mikor felfedezzük hogyan viszonyulunk a világhoz és más emberekhez. Ekkor tanuljuk meg, mit jelent a siker, és hogyan érjük el azt. Ekkor alakítjuk ki saját értékrendünket, és elsõ én-képünket. Nyilvánvaló, hogy az iskola nem az egyetlen befolyásoló tényezõ ebben – a szüleink és kortársaink sokkal erõsebb hatások – de elég nagy a szerepe.
|
||||
|
||||
Ha az iskolára nem mint információ-forrásra tekintünk, hanem mint olyan helyre, ahol önmagunkat ismerjük meg, felfedezhetjük, hogy több leckét is megtanulunk anélkül, hogy észrevennénk.
|
||||
|
||||
1. Megtanultad, hogy a siker mások elismerésébõl fakad
|
||||
|
||||
Látszólag olyan világban élünk, ahol az embereknek sokkal fontosabb az, hogy fontos svemélyeknek látsszanak, mint az, hogy valóban fontos személyek legyenek. Ilyenek pl. a Kardashian nõvérek, Donald Trump, az Instagram felhasználók 63%-a, rap-albumot készítõ atléták, az USA teljes kongresszusa, stb.
|
||||
|
||||
Ennek rengeteg oka van, de a legfontosabb hogy mialatt felnövünj, mások normáinak való megfelelésünkért vagyunk jutalmazva és büntetve, nem pedig a sajátunk alapján. Legyenek jó jegyeid. Vegyél résvt haladó kurzusokon. Játssz sport-csapatokban. Érj el magas pontszámot a standardizált teszteken. Ezek a mérõszámok produktív munkaerõt teremtenek, de nem boldog munkaerõt.
|
||||
|
||||
Az élet miértjei sokkal fontosabbak, mint az élet mijei; és ezt az üzenetet nagyon ritkán halljuk, míg felnövünk.
|
||||
|
||||
Lehetsz a legjobb reklámügynök a világon, ha haszontalan pénisz-japszulákat reklámozol, a tehetséged a társadalom számára nem eszköz, hanem teher. Lehetsz a világ legjobb befektetõqe' ha olyan országokban vagy olyan vállalatokba fektetsz be ahol korrupcióval és emberkereskedelemmel termelik a profitot, a tehetséged a társadalom számára nem eszköz, hanem teher. Lehetsz a világ legjobb elõadója, de da vallási fanatizmusról és rasszizmusról tanítasz, a tehetséged a társadalom számára nem eszköz, hanem teher.
|
||||
|
||||
[Image: Frustrated child with learning difficulties]
|
||||
|
||||
Míg felnősz, minden, amit mondanak hogy csinálj kizárólag azt a célt szolgálja, hogy kivívd mások elismerését. Arról szól, Growing up, everything you’re told to do is for no other purpose than to
|
||||
earn the approval of others around you. It’s to satisfy somebody else’s
|
||||
standard. How many times growing up did you ever hear the complaint, “This
|
||||
is pointless. Why do I have to learn this?” How many times do I hear adults
|
||||
saying, “I don’t even know what I like to do, all I know is I’m not happy.”
|
||||
|
||||
Our system is performance-based and not purpose-based. It teaches mimicry
|
||||
and not passion.
|
||||
|
||||
Performance-based learning isn’t even efficient. A kid who is excited about
|
||||
cars is going to have a hell of a much better time learning about math and
|
||||
physics if math and physics can be put into the context of what he cares
|
||||
about. He’s going to retain more of it and become curious to discover more
|
||||
on his own.
|
||||
|
||||
But if he isn’t responsible for the why of what he is learning, then what
|
||||
he’s learning isn’t physics and math, it’s how to fake it to make someone
|
||||
else happy. And that’s an ugly habit to engrain into a culture. It churns
|
||||
out a mass of highly-efficient, low self-esteem people.
|
||||
|
||||
In the past few decades, concerned parents and teachers have tried to remedy
|
||||
this “self-esteem” issue by making it easier for kids to feel
|
||||
successful. But this just makes the problem worse. Not only are you training
|
||||
kids to base their self-worth on the approval of others, but now you’re
|
||||
giving them that approval without them having to actually do anything to
|
||||
earn it!
|
||||
|
||||
Or as Branford Marsalis, one of the greatest saxophone players of all-time,
|
||||
so eloquently put it:
|
||||
|
||||
The emphasis on success as external performance is a vestige of the
|
||||
industrial age — it molded kids into pliant worker bees, not happy
|
||||
individuals. It doesn’t make sense anymore.
|
||||
|
||||
External performance markers are fine, and likely even necessary, but
|
||||
they’re not sufficient anymore. There has to be a new starting point. There
|
||||
has to be personal purpose introduced into education at some point. There
|
||||
needs to be a why to learning to go with the what. The problem is that
|
||||
everybody’s why is personal and it’s impossible to scale. Especially when
|
||||
teachers are so over-worked and underpaid.
|
||||
|
||||
2. YOU LEARNED THAT FAILURE IS A SOURCE OF SHAME
|
||||
|
||||
Earlier this year I had lunch with one of those people that you just can’t
|
||||
believe exists. He had four degrees, including a masters from MIT and a PhD
|
||||
from Harvard (or was it a masters from Harvard and a PhD from MIT? I can’t
|
||||
even remember). He was at the top of his field, worked for one of the most
|
||||
prestigious consulting firms and had traveled all over the world working
|
||||
with top CEOs and managers.
|
||||
|
||||
And then he told me he felt stuck. He wanted to start a business but he
|
||||
didn’t know how.
|
||||
|
||||
And he wasn’t stuck because he didn’t know what to do. He knew exactly what
|
||||
he wanted to do. He was stuck because he didn’t know if it was the right
|
||||
thing to do.
|
||||
|
||||
He told me that throughout his entire life he had mastered the art of
|
||||
getting it right on the first try. That’s how schools reward you. That’s how
|
||||
companies reward you. They tell you what to do and then you nail it. And he
|
||||
could always nail it.
|
||||
|
||||
But when it came to creating something new, doing something innovative,
|
||||
stepping out into the unknown, he didn’t know how to do it. He was
|
||||
afraid. Innovation requires failure, and he didn’t know how to do
|
||||
failure. He had never failed before!
|
||||
|
||||
In his new book, Malcolm Gladwell wrote a chapter about how a
|
||||
disproportionate number of insanely successful people are dyslexic and/or
|
||||
high school drop outs. Gladwell suggested a simple explanation: these were
|
||||
talented people who, for whatever reason, were forced to become accustomed
|
||||
to failure early on in their lives. This comfort with failure allowed them
|
||||
to take more calculated risks and see opportunities where others weren’t
|
||||
looking later on.
|
||||
|
||||
Failure helps us. It’s how we learn. Failed job applications teach us how to
|
||||
be better applicants. Failed relationships teach us how to be better
|
||||
partners. Launching products or services that bomb teach us how to make
|
||||
better products and services. Failure is the path to growth. Yet we get it
|
||||
hammered into our brains over and over that failure is always
|
||||
unacceptable. That being wrong is shameful. That you get one shot and if you
|
||||
screw it up, it’s over, you get a bad grade and that’s it.
|
||||
|
||||
But that’s not how life works at all.
|
||||
|
||||
3. YOU LEARNED TO DEPEND ON AUTHORITY
|
||||
|
||||
Sometimes I get emails from readers who send me their life stories and then
|
||||
ask me to tell them what to do. Their situations are usually impossibly
|
||||
personal and complex. And so my answer is usually, “I have no clue.” I don’t
|
||||
know these people. I don’t know what they’re like. I don’t know what their
|
||||
values are or how they feel or where they come from. I’m just some guy who
|
||||
writes obnoxious shit on the internet to get more Facebook likes. How would
|
||||
I know?
|
||||
|
||||
I think there’s a tendency for most of us to be scared of not having someone
|
||||
tell us what to do. Being told what to do can be comfortable. It can feel
|
||||
safe because ultimately, you never feel entirely responsible for your
|
||||
fate. You’re just following the game plan.
|
||||
|
||||
Taught in school
|
||||
|
||||
Dependence on authority, like focusing on performance over purpose, is a
|
||||
vestige of our industrial history. Obedience was a major societal value
|
||||
100-200 years ago. It was necessary for society to thrive.
|
||||
|
||||
Now blind obedience causes more problems than it solves. It kills creative
|
||||
thinking. It promotes mindless parroting and inane certainty. It keeps crap
|
||||
TV on the air.
|
||||
|
||||
That doesn’t mean authority is always harmful. It doesn’t mean that
|
||||
authority serves no purpose. Authority will always exist and will always be
|
||||
necessary for a well-functioning society.
|
||||
|
||||
But we should all be capable of choosing the authority in our
|
||||
lives. Adherence to authority should never be compulsory, and it should
|
||||
never go unquestioned — whether they’re your preacher, your boss, your
|
||||
teacher or your best friend. No one knows what’s right for you as well as
|
||||
you do. And not letting kids discover that fact for themselves may be the
|
||||
biggest failure of all.
|
27
_posts/2017-10-02-add-sysadmin-day-to-emacs-calendar.md
Normal file
27
_posts/2017-10-02-add-sysadmin-day-to-emacs-calendar.md
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Add SysAdmin day to Emacs Calendar"
|
||||
date: 2017-10-02 09:37:52
|
||||
tags: [emacs]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
I’m a SysAdmin since 1998. Maybe a bit earlier, if you count managing our home computer. This
|
||||
means [SysAdmin Day](http://sysadminday.com/) is also celebrating me. However, my Emacs Calendar
|
||||
doesn’t show it for some reason.
|
||||
|
||||
The solution is pretty easy:
|
||||
|
||||
``` lisp
|
||||
(add-to-list 'holiday-other-holidays '(holiday-float 7 5 -1 "SysAdmin Day") t)
|
||||
```
|
||||
|
||||
Now invoke `holidays-list` for any year, choosing “Other” as the category, and there you go:
|
||||
|
||||
```
|
||||
…
|
||||
Friday, July 28, 2017: SysAdmin Day
|
||||
…
|
||||
```
|
@ -1,19 +1,19 @@
|
||||
Please welcome Calendar.social
|
||||
##############################
|
||||
|
||||
:date: 2018-06-26T05:36:00Z
|
||||
:category: blog
|
||||
:tags: development
|
||||
:url: 2018/06/26/please-welcome-calendar-social/
|
||||
:save_as: 2018/06/26/please-welcome-calendar-social/index.html
|
||||
:status: published
|
||||
:author: Gergely Polonkai
|
||||
---
|
||||
layout: post
|
||||
title: "Please welcome Calendar.social"
|
||||
date: 2018-06-26 05:36:00
|
||||
tags: [development]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I started looking at decentralised/federated tools some years ago, but other than Matrix I didn’t
|
||||
use any of them until recently. Then this February I joined the Fediverse (federated universe) by
|
||||
spinning up my own `Mastodon <https://joinmastodon.org/>`_ instance. I’m not going to lie, this
|
||||
spinning up my own [Mastodon](https://joinmastodon.org/) instance. I’m not going to lie, this
|
||||
place is pure awesome. I follow only 53 people but unlike on Twitter or Facebook, I can’t just
|
||||
scroll through my timeline; I have to read them all. These 53 accounts are real persons over the
|
||||
scroll through my timeline; I have to read the all. These 53 accounts are real persons over the
|
||||
Internet with meaningful posts. I could never find this on the noisy Twitter or Facebook timeline
|
||||
during the last 10 or so years.
|
||||
|
||||
@ -30,25 +30,25 @@ later.)
|
||||
I have a *lot* on my mind about what this tool should/could do when it’s done. In no particular
|
||||
order, here’s a list of them:
|
||||
|
||||
* events that can be private (only you and the (optional) guests see them), or public (anyone can
|
||||
- events that can be private (only you and the (optional) guests see them), or public (anyone can
|
||||
see them). They will have all the usual fields like start/end time, location, and maybe an icon
|
||||
and a cover photo
|
||||
* multiple calendars you would expect from a calendar app. This way you can separate your
|
||||
- multiple calendars you would expect from a calendar app. This way you can separate your
|
||||
birthday reminders from the dentist appointments
|
||||
* event sharing over ActivityPub and other channels (to be decided, but I think you can take email
|
||||
- event sharing over ActivityPub and other channels (to be decided, but I think you can take email
|
||||
and maybe Matrix for granted.)
|
||||
* full calendar sharing. The other party can get access from a very basic free/busy level to full
|
||||
- full calendar sharing. The other party can get access from a very basic free/busy level to full
|
||||
write access (which might be a good idea for family or company wide calendars.)
|
||||
* Holiday calendars that store national/bank holidays. Users can subscribe to them to see the
|
||||
- Holiday calendars that store national/bank holidays. Users can subscribe to them to see the
|
||||
holidays of a given country/area, and optionally set them as busy (on holiday weekdays) or free
|
||||
(on weekends that are actually workdays for some reason.)
|
||||
* Reminders! Because you obviously don’t want to forget the birthday of your significant other,
|
||||
- Reminders! Because you obviously don’t want to forget the birthday of your significant other,
|
||||
your anniversary, or your barber appointment.
|
||||
* All this developed with time zones, localisation, and accessibility in mind.
|
||||
- All this developed with time zones, localisation, and accessibility in mind.
|
||||
|
||||
That, and anything more that comes to my mind.
|
||||
|
||||
You can follow the development `here <https://gitea.polonkai.eu/gergely/calendar-social>`_. Also,
|
||||
feel free to ping me with your ideas on my `Mastodon account <https://social.polonkai.eu/@gergely>`_, `Matrix
|
||||
<https://matrix.to/#/@gergely:polonkai.eu>`_, or any other channels you can find under the
|
||||
You can follow the development [here](https://gitea.polonkai.eu/gergely/calendar-social). Also,
|
||||
feel free to ping me with your ideas on [Mastodon](https://social.polonkai.eu/@gergely),
|
||||
[Matrix](https://matrix.to/#/@gergely:polonkai.eu), or any other channels you can find under the
|
||||
“Contact me” menu.
|
214
_posts/2018-07-26-check-if-last-git-commit-has-test-coverage.md
Normal file
214
_posts/2018-07-26-check-if-last-git-commit-has-test-coverage.md
Normal file
@ -0,0 +1,214 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Check if the last Git commit has test coverage"
|
||||
date: 2018-07-26 12:49:52
|
||||
tags: [python,development,testing]
|
||||
published: true
|
||||
author:
|
||||
name: Gergely Polonkai
|
||||
email: gergely@polonkai.eu
|
||||
---
|
||||
|
||||
I use Python at work and for private projects. I also aim to write tests for my code, especially
|
||||
recently. And as I usually don’t start from 100% code coverage (TDD is not my game), I at least
|
||||
want to know if the code I just wrote have full coverage.
|
||||
|
||||
The trick is to collect all the lines that changed, and all the lines that has no coverage. Then
|
||||
compare the two, and you have the uncovered lines that changed!
|
||||
|
||||
### Getting the list of changed lines
|
||||
|
||||
Recently, I bumped into
|
||||
[this article](https://adam.younglogic.com/2018/07/testing-patch-has-test/). It is a great awk
|
||||
script that lists the lines that changed in the latest commit. I have really no problem with awk,
|
||||
but I’m pretty sure it can be done in Python, as that is my main language nowadays.
|
||||
|
||||
```python
|
||||
def get_changed_lines():
|
||||
"""Get the line numbers that changed in the last commit
|
||||
"""
|
||||
|
||||
git_output = subprocess.check_output('git show', shell=True).decode('utf-8')
|
||||
|
||||
current_file = None
|
||||
lines = {}
|
||||
left = 0
|
||||
right = 0
|
||||
|
||||
for line in git_output.split('\n'):
|
||||
match = re.match(r'^@@ -([0-9]+),[0-9]+ [+]([0-9]+),[0-9]+ @@', line)
|
||||
|
||||
if match:
|
||||
left = int(match.groups()[0])
|
||||
right = int(match.groups()[1])
|
||||
|
||||
continue
|
||||
|
||||
if re.match(r'^\+\+\+', line):
|
||||
current_file = line[6:]
|
||||
|
||||
continue
|
||||
|
||||
if re.match(r'^-', line):
|
||||
left += 1
|
||||
|
||||
continue
|
||||
|
||||
if re.match(r'^[+]', line):
|
||||
# Save this line number as changed
|
||||
lines.setdefault(current_file, [])
|
||||
lines[current_file].append(right)
|
||||
right += 1
|
||||
|
||||
continue
|
||||
|
||||
left += 1
|
||||
right += 1
|
||||
|
||||
return lines
|
||||
```
|
||||
|
||||
OK, not as short as the awk script, but works just fine.
|
||||
|
||||
### Getting the uncovered lines
|
||||
|
||||
Coverage.py can list the uncovered lines with `coverage report --show-missing`. For Calendar.social, this looks something like this:
|
||||
|
||||
```
|
||||
Name Stmts Miss Cover Missing
|
||||
----------------------------------------------------------------------
|
||||
calsocial/__init__.py 173 62 64% 44, 138-148, 200, 239-253, 261-280, 288-295, 308-309, 324-346, 354-363
|
||||
calsocial/__main__.py 3 3 0% 4-9
|
||||
calsocial/account.py 108 51 53% 85-97, 105-112, 125, 130-137, 148-160, 169-175, 184-200, 209-212, 221-234
|
||||
calsocial/app_state.py 10 0 100%
|
||||
calsocial/cache.py 73 11 85% 65-70, 98, 113, 124, 137, 156-159
|
||||
calsocial/calendar_system/__init__.py 10 3 70% 32, 41, 48
|
||||
calsocial/calendar_system/gregorian.py 77 0 100%
|
||||
calsocial/config_development.py 11 11 0% 4-17
|
||||
calsocial/config_testing.py 12 0 100%
|
||||
calsocial/forms.py 198 83 58% 49, 59, 90, 136-146, 153, 161-169, 188-195, 198-206, 209-212, 228-232, 238-244, 252-253, 263-267, 273-277, 317-336, 339-342, 352-354, 362-374, 401-413
|
||||
calsocial/models.py 372 92 75% 49-51, 103-106, 177, 180-188, 191-200, 203, 242-248, 257-268, 289, 307, 349, 352-359, 378, 392, 404-409, 416, 444, 447, 492-496, 503, 510, 516, 522, 525, 528, 535-537, 545-551, 572, 606-617, 620, 652, 655, 660, 700, 746-748, 762-767, 774-783, 899, 929, 932
|
||||
calsocial/security.py 15 3 80% 36, 56-58
|
||||
calsocial/utils.py 42 5 88% 45-48, 52-53
|
||||
----------------------------------------------------------------------
|
||||
TOTAL 1104 324 71%
|
||||
```
|
||||
|
||||
All we have to do is converting these ranges into a list of numbers, and compare it with the
|
||||
result of the previous function:
|
||||
|
||||
```python
|
||||
def get_uncovered_lines(changed_lines):
|
||||
"""Get the full list of lines that has not been covered by tests
|
||||
"""
|
||||
|
||||
column_widths = []
|
||||
uncovered_lines = {}
|
||||
|
||||
for line in sys.stdin:
|
||||
line = line.strip()
|
||||
|
||||
if line.startswith('---'):
|
||||
continue
|
||||
|
||||
if line.startswith('Name '):
|
||||
match = re.match(r'^(Name +)(Stmts +)(Miss +)(Cover +)Missing$', line)
|
||||
assert match
|
||||
|
||||
column_widths = [len(col) for col in match.groups()]
|
||||
|
||||
continue
|
||||
|
||||
name = [
|
||||
line[sum(column_widths[0:idx]):sum(column_widths[0:idx]) + width].strip()
|
||||
for idx, width in enumerate(column_widths)][0]
|
||||
missing = line[sum(column_widths):].strip()
|
||||
|
||||
for value in missing.split(', '):
|
||||
if not value:
|
||||
continue
|
||||
|
||||
try:
|
||||
number = int(value)
|
||||
except ValueError:
|
||||
first, last = value.split('-')
|
||||
lines = range(int(first), int(last) + 1)
|
||||
else:
|
||||
lines = range(number, number + 1)
|
||||
|
||||
for lineno in lines:
|
||||
if name in changed_lines and lineno not in changed_lines[name]:
|
||||
uncovered_lines.setdefault(name, [])
|
||||
uncovered_lines[name].append(lineno)
|
||||
|
||||
return uncovered_lines
|
||||
```
|
||||
|
||||
At the end we have a dictionary that has filenames as keys, and a list of changed but uncovered
|
||||
lines.
|
||||
|
||||
### Converting back to ranges
|
||||
|
||||
To make the final result more readable, let’s convert them back to a nice `from_line-to_line`
|
||||
range list first:
|
||||
|
||||
```python
|
||||
def line_numbers_to_ranges():
|
||||
"""List the lines that has not been covered
|
||||
"""
|
||||
|
||||
changed_lines = get_changed_lines()
|
||||
uncovered_lines = get_uncovered_lines(changed_lines)
|
||||
|
||||
line_list = []
|
||||
|
||||
for filename, lines in uncovered_lines.items():
|
||||
lines = sorted(lines)
|
||||
last_value = None
|
||||
|
||||
ranges = []
|
||||
|
||||
for lineno in lines:
|
||||
if last_value and last_value + 1 == lineno:
|
||||
ranges[-1].append(lineno)
|
||||
else:
|
||||
ranges.append([lineno])
|
||||
|
||||
last_value = lineno
|
||||
|
||||
range_list = []
|
||||
|
||||
for range_ in ranges:
|
||||
first = range_.pop(0)
|
||||
|
||||
if range_:
|
||||
range_list.append(f'{first}-{range_[-1]}')
|
||||
else:
|
||||
range_list.append(str(first))
|
||||
|
||||
line_list.append((filename, ', '.join(range_list)))
|
||||
|
||||
return line_list
|
||||
```
|
||||
|
||||
### Printing the result
|
||||
|
||||
Now all that is left is to print the result on the screen in a format digestable by a human being:
|
||||
|
||||
```python
|
||||
def tabular_print(uncovered_lines):
|
||||
"""Print the list of uncovered lines on the screen in a tabbed format
|
||||
"""
|
||||
|
||||
max_filename_len = max(len(data[0]) for data in uncovered_lines)
|
||||
|
||||
for filename, lines in uncovered_lines:
|
||||
print(filename.ljust(max_filename_len + 2) + lines)
|
||||
```
|
||||
|
||||
And we are done.
|
||||
|
||||
### Conclusion
|
||||
|
||||
This task never seemed hard to accomplish, but somehow I never put enough energy into it to make
|
||||
it happen. Kudos to Adam Young doing some legwork for me!
|
24
about.html
Normal file
24
about.html
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
layout: page
|
||||
title: About the author
|
||||
permalink: /about/
|
||||
---
|
||||
{% include about.html %}
|
||||
|
||||
<h3>Some fun stats and links</h3>
|
||||
|
||||
<h4>I’m a registered Linux User :)</h4>
|
||||
<p>
|
||||
<a href="https://www.linuxcounter.net/user/416972">
|
||||
<img src="https://www.linuxcounter.net/cert/416972.png"
|
||||
alt="I am Linux user #416972">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h4>I have my own GeekCode</h4>
|
||||
<pre>
|
||||
-----BEGIN GEEK CODE BLOCK-----
|
||||
Version: 3.1
|
||||
GCM/CS/IT/O d--(+) s++:- a C++$ UB++L++++$ P++ L+++$ E++>$ W+++$ N o? K w+ O M- V PS+ PE Y+ PGP+(++) t+ 5 X R !tv b+ DI++ D+ G e h----() r+++ y++++
|
||||
-----END GEEK CODE BLOCK-----
|
||||
</pre>
|
30
blog/atom.xml
Normal file
30
blog/atom.xml
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
---
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>{{site.title | xml_escape}}</title>
|
||||
<description>{{site.description | xml_escape}}</description>
|
||||
<link>{{site.baseurl | prepend: site.url}}</link>
|
||||
<atom:link href="{{'/blog/atom.xml' | prepend: site.baseurl | prepend: site.url}}" rel="self" type="application/rss+xml" />
|
||||
<lastBuildDate>{{site.posts.first.date | date: "%a, %d %b %Y %H:%M:%S %z"}}</lastBuildDate>
|
||||
<pubDate>{{site.posts.first.date | date: "%a, %d %b %Y %H:%M:%S %z"}}</pubDate>
|
||||
<ttl>7200</ttl>
|
||||
<language>en</language>
|
||||
<image>
|
||||
<title>Gergely Polonkai</title>
|
||||
<url>{{'/images/profile.png' | prepend: site.baseurl | prepend: site.url}}</url>
|
||||
<link>{{'/' | prepend: site.baseurl | prepend: site.url}}</link>
|
||||
</image>
|
||||
{% for post in site.posts limit:10 %}
|
||||
<item>
|
||||
<title>{{post.title | xml_escape}}</title>
|
||||
<link>{{post.url | prepend: site.baseurl | prepend: site.url}}</link>
|
||||
<comments>{{post.url | prepend: site.baseurl | prepend: site.url}}#comments</comments>
|
||||
<pubDate>{{post.date | date: "%a, %d %b %Y %H:%M:%S %z"}}</pubDate>
|
||||
<description>{{post.excerpt | xml_escape}}</description>
|
||||
<guid isPermaLink="true">{{post.url | prepend: site.baseurl | prepend: site.url}}</guid>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</channel>
|
||||
</rss>
|
12
blog/index.html
Normal file
12
blog/index.html
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
layout: page
|
||||
title: Blog posts
|
||||
post_listing: true
|
||||
---
|
||||
{% include pagination.html %}
|
||||
|
||||
{% assign posts = paginator.posts %}
|
||||
|
||||
{% include post-list.html %}
|
||||
|
||||
{% include pagination.html %}
|
4
blog/tag/active-directory.md
Normal file
4
blog/tag/active-directory.md
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
layout: posts-by-tag
|
||||
tag: active-directory
|
||||
---
|
4
blog/tag/apache.md
Normal file
4
blog/tag/apache.md
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
layout: posts-by-tag
|
||||
tag: apache
|
||||
---
|
4
blog/tag/astrology.md
Normal file
4
blog/tag/astrology.md
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
layout: posts-by-tag
|
||||
tag: astrology
|
||||
---
|
4
blog/tag/c.md
Normal file
4
blog/tag/c.md
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
layout: posts-by-tag
|
||||
tag: c
|
||||
---
|
4
blog/tag/citrix-xenserver.md
Normal file
4
blog/tag/citrix-xenserver.md
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
layout: posts-by-tag
|
||||
tag: citrix-xenserver
|
||||
---
|
4
blog/tag/command-line.md
Normal file
4
blog/tag/command-line.md
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
layout: posts-by-tag
|
||||
tag: command-line
|
||||
---
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user