-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfeed.xml
385 lines (310 loc) · 39.1 KB
/
feed.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Dan Stefaniuk</title>
<description>A blog about software development.
</description>
<link>http://stefaniuk.co.uk/</link>
<atom:link href="http://stefaniuk.co.uk/feed.xml" rel="self" type="application/rss+xml"/>
<pubDate>Sat, 02 Sep 2017 22:42:47 +0100</pubDate>
<lastBuildDate>Sat, 02 Sep 2017 22:42:47 +0100</lastBuildDate>
<generator>Jekyll v3.4.3</generator>
<item>
<title>A Revised Way of Generating SSH Keys</title>
<description><p>I&#39;ve been using 4096-bit RSA SSH keys for quite a few years. The RSA keys are very compatible. They&#39;ve been working on various operating systems as well as on mobile devices. They are also known as being slow and potentially insecure if created with a small amount of bits, especially after the year 2013. Today, I decided to do some research to find an alternative configuration. Things have moved on since my last check. Large keys are still considered secure, however the <a href="https://en.wikipedia.org/wiki/Elliptic_curve_cryptography"><strong>elliptic curve cryptography</strong></a> has become much more popular in the recent decade.</p>
<p><em>&quot;The primary benefit promised by elliptic curve cryptography is a smaller key size, reducing storage and transmission requirements, i.e. that an elliptic curve group could provide the same level of security afforded by an RSA-based system with a large modulus and correspondingly larger key: for example, a 256-bit elliptic curve public key should provide comparable security to a 3072-bit RSA public key.&quot;</em></p>
<p>My take on it is that if this new signature algorithm can give us similar or even <strong>better level of security</strong> (yes, its implementation is more secure than RSA) with a <strong>comparable flexibility</strong> using <strong>less resources</strong>, it is definitely time to adopt it. For anyone who is interested in a detailed explanation how exactly it works a lot of papers have been published providing mathematical rationale behind it.</p>
<p><a href="https://ed25519.cr.yp.to/"><strong>Ed25519</strong></a> is a digital signature scheme based on Twisted Edwards curves. Its has been <a href="https://github.com/openssh/openssh-portable/commit/5be9d9e3cbd9c66f24745d25bf2e809c1d158ee0#diff-e71776f50c4432cb9cd999367424de20">added to the OpenSSH codebase</a> on the 7th of December 2013. Since then no issue was reported against it.</p>
<p>The easies way to generate such an SSH key is by using the <code>ssh-keygen</code> command of the OpenSSH package:</p>
<div class="highlight"><pre><code class="language-" data-lang="">ssh-keygen -t ed25519 -o -a 100
</code></pre></div>
<p>The <code>-t ed25519</code> parameter is used to generate two asymmetrical keys based on the elliptic curve cryptography. The output is produced in the new <a href="https://tools.ietf.org/html/rfc4716">RFC4716</a> format rather than the well known PEM and the flag <code>-o</code> to support it is implied by default so it can be omitted. It enforces use of a modern key derivation function (KDF) powered by combination of <a href="https://github.com/openssh/openssh-portable/blob/f104da263de995f66b6861b4f3368264ee483d7f/openbsd-compat/bcrypt_pbkdf.c">PBKDF2 and bcrypt</a>. In practice this means that our keypair is more resistant to brute-force password cracking. This is especially true when combined with the <code>-a &lt;rounds&gt;</code> parameter which specifies the number of key derivation function rounds to be used. Higher the number more time it takes to unlock the key.</p>
<p>Time to see the result of the above command. Here is an example of my revised way of generating SSH keys along with content of the files.</p>
<p>&nbsp;</p>
<div class="terminal">
<nav>
<a href="#" class="close">close</a>
<a href="#" class="minimize">minimize</a>
<a href="#" class="deactivate">deactivate</a>
</nav>
<h3 class="title">Terminal</h3>
<pre>
<span class='command'>ssh-keygen -t ed25519 -a 100 -C &quot;my-key-comment&quot; -f my-key-name</span>
<span class='output'>Generating public/private ed25519 key pair.</span>
<span class='output'>Enter passphrase (empty for no passphrase):</span>
<span class='output'>Enter same passphrase again:</span>
<span class='output'>Your identification has been saved in my-key-name.</span>
<span class='output'>Your public key has been saved in my-key-name.pub.</span>
<span class='output'>The key fingerprint is:</span>
<span class='output'>SHA256:GIh0NDZ6WhZLyFJxq8dpbJ7aAwfEpJdgBsgxs/5NziM my-key-comment</span>
<span class='output'>The key&#39;s randomart image is:</span>
<span class='output'>+--[ED25519 256]--+</span>
<span class='output'>|*@*+X |</span>
<span class='output'>|==OO B |</span>
<span class='output'>|.+= B . |</span>
<span class='output'>|...O . o |</span>
<span class='output'>| .o.B.. S |</span>
<span class='output'>| o=*. |</span>
<span class='output'>| Eo= |</span>
<span class='output'>| oo . |</span>
<span class='output'>| . .. |</span>
<span class='output'>+----[SHA256]-----+</span>
<span class='command'>puttygen my-key-name -C &quot;my-key-comment&quot; -o my-key-name.ppk</span>
<span class='output'>Enter passphrase to load key:</span>
<span class='command'>ls -la my-key-name*</span>
<span class='output'>-rw------- 1 dan staff 464 3 Sep 21:27 my-key-name</span>
<span class='output'>-rw------- 1 dan staff 304 3 Sep 21:27 my-key-name.ppk</span>
<span class='output'>-rw-r--r-- 1 dan staff 96 3 Sep 21:27 my-key-name.pub</span>
<span class='command'>cat my-key-name</span>
<span class='output'>-----BEGIN OPENSSH PRIVATE KEY-----</span>
<span class='output'>b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABA+HlsP7o</span>
<span class='output'>3Jzj8b+N+WhbEuAAAAZAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIErbaYC9ZHLEtCY3</span>
<span class='output'>uDDl0LqRwlxLSGoHO1IBrvhzikyVAAAAoD632B3FjYbZmkLco+r7BVIvK3r1Cn2dJE8Q4N</span>
<span class='output'>l9IES8bieUYPxDi3uu3gaIcWwygdHTM5zFMqWePJgNP2M0jvfLkQLbaJd816D/BYwUGcTR</span>
<span class='output'>3Mgo7Rnf1qqoen70rwl67bbTaN8D0M5dNL5EkYdKvOoiRhoyQEIdptNOWF6rjwMyfloWw9</span>
<span class='output'>JMKCOfYrSUB4pDf6mgbWw7+60IUrIc+BeAQgc=</span>
<span class='output'>-----END OPENSSH PRIVATE KEY-----</span>
<span class='command'>cat my-key-name.pub</span>
<span class='output'>ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIErbaYC9ZHLEtCY3uDDl0LqRwlxLSGoHO1IBrvhzikyV my-key-comment</span>
<span class='command'>cat my-key-name.ppk</span>
<span class='output'>PuTTY-User-Key-File-2: ssh-ed25519</span>
<span class='output'>Encryption: aes256-cbc</span>
<span class='output'>Comment: my-key-comment</span>
<span class='output'>Public-Lines: 2</span>
<span class='output'>AAAAC3NzaC1lZDI1NTE5AAAAIErbaYC9ZHLEtCY3uDDl0LqRwlxLSGoHO1IBrvhz</span>
<span class='output'>ikyV</span>
<span class='output'>Private-Lines: 1</span>
<span class='output'>63bsFlto1i20tXwFu3xveXsGtDD7hEmsM0FrIGqviHn4O2xXG9Pwjmhmwf+z8rJe</span>
<span class='output'>Private-MAC: 72102d9a0f158f653577df276fcf329b482df329</span>
</pre>
</div>
</description>
<pubDate>Sat, 02 Sep 2017 22:16:00 +0100</pubDate>
<link>http://stefaniuk.co.uk/a-revised-way-of-generating-ssh-keys</link>
<guid isPermaLink="true">http://stefaniuk.co.uk/a-revised-way-of-generating-ssh-keys</guid>
</item>
<item>
<title>Create Consistent SSH User Accounts Across Multiple Linux Servers</title>
<description><p>Due to a specific design requirements on a project I&#39;ve been working on recently I needed to create a consistent system account on all the Linux servers across all the environments. This account supposed to have the following characteristics:</p>
<ul>
<li>Same GID and UID</li>
<li>Both password and public SSH key are set</li>
<li>Only the SSH key-based authentication is allowed</li>
<li>Privileged operations are executed without having to provide password (optional)</li>
</ul>
<p>As you can tell this is an admin type account that is usually created just after the installation of an operating system.</p>
<p>Servers are up and running. Setting it up by hand could be an option. However, this would be a very old-fashioned approach though. I already have an automation tool in place. My favorite one is Ansible due to its simple architecture. Whether or not our configuration management framework is really advanced and facilitates tweaking of every aspect of an OS or a VM in my opinion the best language to script it is Bash. The change will be expressed in a most readable way that every sysadmin or platform engineer as they are called these days will understand. I&#39;m going to use Ansible only as an orchestration tool to ship the script across and execute it without any user interaction.</p>
<p>Here is our sample input:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">id</span><span class="o">=</span>1000
<span class="nv">username</span><span class="o">=</span><span class="s2">"dan"</span>
<span class="nv">password</span><span class="o">=</span><span class="s2">"s3cr3t"</span>
<span class="nv">ssh_pk</span><span class="o">=</span><span class="s2">"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGA++RFH4MfQ+697dSNS+p2Hx0a4X56pnDrMIbDozYUPcbY6rictsJvsakaF2yMlR9xW0nEWichdwB5dFxoVQ9E6n6ygRB5Q88j+SOsvVA+wjCJ0+Ittfrb9/dTrnuSWT58p1HKaCDOJdx402P41DZGw4Fq+JKTEvP+H0AbOdUifNMrZBPZ0kOv/0bNT839ytI6rKGi+3w42TN37k7H02TmAFPnip3YpLrUMNxMHulxyzaQ2ueAILGmPac2pz6hU6OflSCkptiV7yDYv/LnjFDSzagFP0dIr5jkT+XenpJOXgdXA/g3/4aOUHfo9tEQngC9ANKbaB3pRwAfGM35V35 my-key"</span></code></pre></figure>
<p>But before we create such a user let&#39;s check if the ID that we intent to use is available. If it is already taken we need to find the current user, terminate all his processes, change the UID and GID, then fix file ownership accordingly.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">uname</span><span class="o">=</span><span class="k">$(</span>grep <span class="nv">$id</span> /etc/passwd | cut -f1 -d:<span class="k">)</span>
<span class="nv">gname</span><span class="o">=</span><span class="k">$(</span>grep <span class="nv">$id</span> /etc/group | cut -f1 -d:<span class="k">)</span>
ps -o pid -u <span class="nv">$uname</span> | xargs <span class="nb">kill</span> -9
<span class="k">if</span> <span class="o">[</span> -n <span class="s2">"</span><span class="nv">$uname</span><span class="s2">"</span> <span class="o">]</span>; <span class="k">then
</span>usermod -u 1111 <span class="nv">$uname</span>
find / -user <span class="nv">$id</span> -exec chown -h 1111 <span class="o">{}</span> <span class="se">\;</span>
<span class="k">fi
if</span> <span class="o">[</span> -n <span class="s2">"</span><span class="nv">$gname</span><span class="s2">"</span> <span class="o">]</span>; <span class="k">then
</span>groupmod -g 1111 <span class="nv">$gname</span>
find / -group <span class="nv">$id</span> -exec chgrp -h 1111 <span class="o">{}</span> <span class="se">\;</span>
<span class="k">fi</span></code></pre></figure>
<p>Now, we are ready to create a new system user.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">groupadd -g <span class="nv">$id</span> <span class="nv">$username</span>
useradd -m -s /bin/bash -u <span class="nv">$id</span> -g <span class="nv">$username</span> -G sudo,docker <span class="nv">$username</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$username</span><span class="s2">:</span><span class="nv">$password</span><span class="s2">"</span> | chpasswd</code></pre></figure>
<p>SSH key-based authentication provides cryptographic strength that even extremely long passwords cannot offer. So, this will be our preferred method of granting access to a system and ...</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">mkdir -p /home/<span class="nv">$username</span>/.ssh
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$ssh_pk</span><span class="s2">"</span> &gt;&gt; /home/<span class="nv">$username</span>/.ssh/authorized_keys
chmod 600 /home/<span class="nv">$username</span>/.ssh/authorized_keys
chown -R <span class="nv">$username</span>:<span class="nv">$username</span> /home/<span class="nv">$username</span></code></pre></figure>
<p>... we make sure the above is the only method of authentication by changing the OpenSSL daemon configuration.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">sed -i <span class="s2">"s/^PasswordAuthentication yes/#PasswordAuthentication yes/g"</span> <span class="se">\</span>
/etc/ssh/sshd_config</code></pre></figure>
<p>The last thing we may want to do is to allow privileged operations without password. This may come handy while the same system account is used to perform other automated tasks that normally would prompt for a user password.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$username</span><span class="s2"> ALL=(ALL) NOPASSWD: ALL"</span> &gt; /etc/sudoers.d/<span class="nv">$username</span></code></pre></figure>
<p>There we have it. A simple way to test it could be by loading the private key locally using the <code>ssh-add</code> command and trying to <code>ssh</code> into the server.</p>
<p>In my next blog post I will show how to create a minimal Ansible orchestration script.</p>
</description>
<pubDate>Tue, 22 Aug 2017 22:29:00 +0100</pubDate>
<link>http://stefaniuk.co.uk/create-consistent-ssh-user-across-linux-servers</link>
<guid isPermaLink="true">http://stefaniuk.co.uk/create-consistent-ssh-user-across-linux-servers</guid>
</item>
<item>
<title>An Alternative Approach to Estimates</title>
<description><h2>Problem</h2>
<p>A while ago I was asked to plan one of the last phases of development on a project that required re-architecture of the data access layer. One of my tasks was to estimate an effort to replace code that communicates with a legacy back-end. The new code, instead of directly connecting to a database should consume JSON-based REST API endpoints. Due to time constraints and to mitigate the risk to bring the service down this change supposed to be done in as non-invasive way as possible.</p>
<p>Objectives</p>
<ul>
<li>Functionality of the application must remain unchanged for the end user</li>
</ul>
<p>Facts</p>
<ul>
<li>It is a simple MVC application built upon a reach domain model</li>
<li>Books like <em><a href="https://www.amazon.co.uk/Design-patterns-elements-reusable-object-oriented/dp/0201633612">Design Patterns</a></em> and <em><a href="https://www.amazon.co.uk/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882">Clean Code</a></em> were probably not that popular at the time of writing the software</li>
</ul>
<p>Issues</p>
<ul>
<li>Developers&#39; skills in our team differ much</li>
<li>Complexity of the code that needs to be refactored varies from module to module</li>
<li>We have never done that before</li>
</ul>
<p>Assumptions</p>
<ul>
<li>We know the product already</li>
<li>Most of the code logic and flow remain the same</li>
<li>It should take no longer to refactor the code than implementing that functionality from scratch</li>
<li>A lot of work is repetitive and follows a pattern</li>
</ul>
<p>How to take the above factors into account and produce accurate estimates for the management?</p>
<h2>Solution</h2>
<p>I was not able to find anyone who faced similar technical problems with the platform we use. This tells me something about our design, anyway... Yes, I heard about <a href="http://csse.usc.edu/tools/COCOMOII.php">COCOMO II - Constructive Cost Model</a>. However, I still decided to come up with my simplistic formula.</p>
<p>&nbsp;&nbsp;
$$\scriptsize
{
\sum_{i=0}^n componentManDays_i = totalManDays
}
$$
$$\scriptsize
{
\frac{linesOfCode_i}{avgLinesOfCodePerHour \times 6h} \times complexityFactor_i \times contingency = componentManDays_i
}
$$
&nbsp;&nbsp;</p>
<p>Why? Because whichever method is used the outcome is always a guesstimate. I found out that <strong>it is so much dependent on motivation of individual team members</strong> as well as their willingness to learn and contribute. This can not be expressed by any mathematical equation.</p>
<p>I usually add 20% contingency to my calculations and set complexity factor to <em>cf=1</em> for a simple code in higher layers of an application architecture, <em>cf=2</em> for components that require more analytical and problem solving skills and up to <em>cf=3</em> for tasks that require attention of a polyglot programmer.</p>
<h2>Automation</h2>
<p>After considering a manual approach of producing my estimates for each module and component I decided to automate that process. Quick, and in my opinion relatively reliable solution was to base the calculations on an ability to change number of lines of code by an average developer in our team in a selected period of time. There are number of issues with this approach. For example, <strong>data for the empirical analysis must come from a project with a comparable architecture, technology stack, complexity and codebase</strong>. Fortunately, we have been working on this software already for over a year delivering new functionality. So, all the data I needed were in the version control system... The only thing I needed to do is write a script that does the rest of the work for me.</p>
<p>Here is the usage example followed by some of the Bash functions to help to extract relevant information from Git. I used an open source project to produce the output.</p>
<p>&nbsp;</p>
<div class="terminal">
<nav>
<a href="#" class="close">close</a>
<a href="#" class="minimize">minimize</a>
<a href="#" class="deactivate">deactivate</a>
</nav>
<h3 class="title">Terminal</h3>
<pre>
<span class='command'>git_print_stats 1y # for Linux use &quot;1 year&quot;</span>
<span class='output'>Developer 1 1 46 +23:-23</span>
<span class='output'>Developer 2 1 79 +44:-35</span>
<span class='output'>Developer 3 0 0 +0:-0</span>
<span class='output'>Developer 4 5 46 +40:-6</span>
<span class='output'>Developer 5 0 0 +0:-0</span>
<span class='output'>Developer 6 1 2 +1:-1</span>
<span class='output'>Developer 7 0 0 +0:-0</span>
<span class='output'>Developer 8 0 0 +0:-0</span>
<span class='output'>Developer 9 0 0 +0:-0</span>
<span class='output'>Developer 10 1 2 +1:-1</span>
<span class='output'>Developer 11 3 17 +9:-8</span>
<span class='output'>Developer 12 2 61 +57:-4</span>
<span class='output'>Developer 13 30 545 +455:-90</span>
<span class='output'>Developer 14 30 545 +455:-90</span>
<span class='output'>Developer 15 4 117 +101:-16</span>
<span class='output'>Developer 16 1 4 +2:-2</span>
<span class='output'>Developer 17 0 0 +0:-0</span>
<span class='output'>Developer 18 1 15 +13:-2</span>
<span class='output'>Developer 19 6 79 +63:-16</span>
<span class='output'>Developer 20 3 16 +8:-8</span>
<span class='command'>git_calc_avg_changes_per_dev 1y</span>
<span class='output'>78</span>
<span class='command'>source_count_lines ./module/A *.ext</span>
<span class='output'>3595</span>
</pre>
</div>
<p>&nbsp;&nbsp;</p>
<p>Calculate average line changes per developer in a given period of time.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="k">function </span>git_calc_avg_changes_per_dev<span class="o">()</span>
<span class="o">{</span>
<span class="nv">data</span><span class="o">=</span><span class="k">$(</span>git_print_stats <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> --csv | awk -F<span class="s1">','</span> <span class="s1">'{ print $3 }'</span><span class="k">)</span>
<span class="nv">sum</span><span class="o">=</span>0; <span class="nv">i</span><span class="o">=</span>0
<span class="k">for </span>n <span class="k">in</span> <span class="nv">$data</span>; <span class="k">do
</span><span class="nv">sum</span><span class="o">=</span><span class="k">$((</span><span class="nv">$sum</span> <span class="o">+</span> <span class="nv">$n</span><span class="k">))</span>; <span class="o">((</span>i++<span class="o">))</span>
<span class="k">done
</span><span class="nb">echo</span> <span class="k">$((</span><span class="nv">$sum</span> <span class="o">/</span> <span class="nv">$i</span><span class="k">))</span>
<span class="o">}</span></code></pre></figure>
<p>Print number of commits and line changes for each individual contributor.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="k">function </span>git_print_stats<span class="o">()</span>
<span class="o">{</span>
<span class="nv">OLDIFS</span><span class="o">=</span><span class="nv">$IFS</span>; <span class="nv">IFS</span><span class="o">=</span><span class="nv">$'</span><span class="se">\n</span><span class="s1">' #'</span>
<span class="k">for </span>committer <span class="k">in</span> <span class="k">$(</span>git_list_committers<span class="k">)</span>; <span class="k">do
</span><span class="nv">cc</span><span class="o">=</span><span class="k">$(</span>git_count_commits <span class="s2">"</span><span class="nv">$committer</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span><span class="k">)</span>
<span class="nv">cl</span><span class="o">=</span><span class="k">$(</span>git_count_line_changes <span class="s2">"</span><span class="nv">$committer</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span><span class="k">)</span>
<span class="k">if</span> <span class="o">[[</span> <span class="nv">$*</span> <span class="o">==</span> <span class="k">*</span><span class="s2">"--csv"</span><span class="k">*</span> <span class="o">]]</span>; <span class="k">then
</span><span class="nv">al</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$cl</span><span class="s2">"</span> | awk <span class="s1">'{ print $1 }'</span><span class="k">)</span>
<span class="nv">dl</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$cl</span><span class="s2">"</span> | awk <span class="s1">'{ print $2 }'</span><span class="k">)</span>
<span class="nb">printf</span> <span class="s2">"</span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2">,%s,%s,%s</span><span class="se">\n</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$committer</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$cc</span><span class="s2">"</span> <span class="nv">$al</span> <span class="nv">$dl</span>
<span class="k">else
</span><span class="nb">printf</span> <span class="s2">"%40s %7s %30s</span><span class="se">\n</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$committer</span><span class="s2">"</span> <span class="nv">$cc</span> <span class="s2">"</span><span class="nv">$cl</span><span class="s2">"</span>
<span class="k">fi
done
</span><span class="nv">IFS</span><span class="o">=</span><span class="nv">$OLDIFS</span>
<span class="o">}</span></code></pre></figure>
<p>List all contributors.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="k">function </span>git_list_committers<span class="o">()</span>
<span class="o">{</span>
git log --all --format<span class="o">=</span><span class="s1">'%cN'</span> | sort -u
<span class="o">}</span></code></pre></figure>
<p>Return number of commits for a given author.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="k">function </span>git_count_commits<span class="o">()</span>
<span class="o">{</span>
git log <span class="se">\</span>
--author<span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="se">\</span>
--since<span class="o">=</span><span class="k">$(</span>date_ago <span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span><span class="k">)</span> <span class="se">\</span>
--until<span class="o">=</span><span class="k">$(</span>date +%Y/%m/%d<span class="k">)</span> <span class="se">\</span>
--no-merges <span class="se">\</span>
--pretty<span class="o">=</span><span class="s1">'%h [%s] &lt;%an&gt;'</span> <span class="se">\</span>
--abbrev-commit <span class="se">\</span>
| wc -l | tr -d <span class="s1">'[[:space:]]'</span>
<span class="o">}</span></code></pre></figure>
<p>Return number of line changes for a given author.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="k">function </span>git_count_line_changes<span class="o">()</span>
<span class="o">{</span>
git log <span class="se">\</span>
--author<span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="se">\</span>
--since<span class="o">=</span><span class="k">$(</span>date_ago <span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span><span class="k">)</span> <span class="se">\</span>
--until<span class="o">=</span><span class="k">$(</span>date +%Y/%m/%d<span class="k">)</span> <span class="se">\</span>
--no-merges <span class="se">\</span>
--pretty<span class="o">=</span><span class="s1">'%h [%s] &lt;%an&gt;'</span> <span class="se">\</span>
--abbrev-commit <span class="se">\</span>
--numstat <span class="se">\</span>
| awk <span class="s1">'NF==3 \
{ plus+=$1; minus+=$2; total+=$1+$2 } END \
{ printf("%d +%d:-%d\n", total, plus, minus) }'</span>
<span class="o">}</span></code></pre></figure>
<p>Calculate date in the past from now.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="k">function </span>date_ago<span class="o">()</span>
<span class="o">{</span>
<span class="k">if</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$OSTYPE</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"darwin"</span><span class="k">*</span> <span class="o">]]</span>; <span class="k">then</span>
<span class="c"># 1y, 12m, 52w, 365d, etc.</span>
date -v-<span class="nv">$1</span> +%Y/%m/%d
<span class="k">else</span>
<span class="c"># 1 year, 12 months, 52 weeks, 365 days, etc.</span>
date +%Y/%m/%d -d <span class="s2">"-</span><span class="nv">$1</span><span class="s2">"</span>
<span class="k">fi</span>
<span class="o">}</span></code></pre></figure>
<p>Count recursively all lines in files for a give file type.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="k">function </span>source_count_lines<span class="o">()</span>
<span class="o">{</span>
find <span class="nv">$1</span> -name <span class="nv">$2</span> -print0 | xargs -0 cat | wc -l
<span class="o">}</span></code></pre></figure>
<p>&nbsp;</p>
</description>
<pubDate>Sun, 03 Jul 2016 21:18:13 +0100</pubDate>
<link>http://stefaniuk.co.uk/an-alternative-approach-to-estimates</link>
<guid isPermaLink="true">http://stefaniuk.co.uk/an-alternative-approach-to-estimates</guid>
</item>
<item>
<title>Hello, World!</title>
<description><p>I decided to blog. This is long overdue. I should have started this years ago. This begs the question why didn&#39;t I? I code at work I code at home. Time is very precious for me... and yes, there are more important things in live than a hobby. But I&#39;m going to try anyway!</p>
<p>There is an objective, self-improvement. Continuous learning is part of being a good programmer. The world around us changes quickly. Coping with change is no longer sufficient. We need to embrace it.</p>
<p>I also believe that keeping notes of what I do will help me gain better understanding of the problems I solve. Maintaining good reading habits and writing a lot of code is one thing. But sharing the knowledge and being able to explain all of that to others requires deep insight - <strong>here is my challenge</strong>!</p>
</description>
<pubDate>Fri, 04 Mar 2016 07:21:52 +0000</pubDate>
<link>http://stefaniuk.co.uk/hello-world</link>
<guid isPermaLink="true">http://stefaniuk.co.uk/hello-world</guid>
</item>
</channel>
</rss>