dev git, git flow, sourcetree

In this post I woud like to talk about a common Git branching strategy: Git Flow.

Basic approach

  • Main diagram of the workflow:

  • The central repo holds two main branches with an infinite lifetime:

    • master
    • develop
  • origin/master is the main branch where the source code of HEAD always reflects a production-ready state
  • origin/develop is the main branch where the source code of HEAD always reflects a state with the latest delivered development changes for the next release (also called as integration branch)
  • When the source code in the develop branch reaches a stable point and is ready to be released, all of the changes should be merged back into master somehow and then tagged with a release number.
  • There are also supporting branches with limited lifetime such as:
    • Feature branches
    • Release branches
    • Hotfix branches
Supporting branch May branch off Must merge back into Naming convention Lifetime Remarks
Feature develop develop anything except master, develop, release-*, or hotfix-* as long as the feature is in development Features should never interact directly with master.
Release develop develop and master release-* preparation of the release Starts after feature-freeze. Allows minor bugfixes. Adding large new features here is strictly prohibited.
Hotfix master develop and master hotfix-* preparation of the release Similar to release in that used to prepare a production release but unplanned.

Automation

Most tools have plugins to support Git Flow. In the example below I will use SourceTree which is available in Windows and Mac.

In this example I’m going to use a GitHub repository called gitflow-automation-test. I’ve already created it in GitHub and in my development environment.

Initialization and working with features

First thing to do with Git Flow in SourceTree is to initialize the repository.

It sounds confusing at first because the git repo itself is already initialized. What it actually does is explained here. It shows a dialog box to configure the plugin:

It also created the local develop branch.

At this point we have the three types of branches explained above to choose from to start. Let’s start with a new feature named “users”

I just added a blank file that represents the changes required for that feature and commmited my changes.

Now that the feature has been implemented, I want to finish the feature. Similar to start, I went to Repository -> Git Flow -> Hg Flow -> Finish Feature

As the options in the window suggests, this action merges the changes into develop and deletes the local branch. It doesn’t push to remote so we have to push it ourselves.

Releasing new version

Now let’s assume we’ve implemented all the features planned for this release and we want to deploy a new version of our application.

To achieve this we start a new release:

Once we are ready to ship this release, we end the release similarly:

We can choose to push to the origin directly too. If we switch to detail view in Sourcetree output we can see a nice summary of actions that took place:

Summary of actions:
- Release branch 'release/1.0' has been merged into 'master'
- The release was tagged '1.0'
- Release tag '1.0' has been back-merged into 'develop'
- Release branch 'release/1.0' has been locally deleted
- 'develop', 'master' and tags have been pushed to 'origin'
- You are now on branch 'develop' 

Working with hotfixes

As the development continues, let’s assume we implemented another feature named “orders”. Finished the feature and pushed to origin. Now we have a new feature in develop. Suddenly we noticed there’s a bug in the production and we need to deploy a hotfix. We cannot create a new release branch off of develop because the new “orders” feature would be deployed along with the bugfix. So we create out hotfix branch off of master which always points to production.

As we can see the code reverted back to master:

So I added another file to represent the changes for the hotfix and finished the hotfix:

And the summary of actions looks like this:

Summary of actions:
- Hotfix branch 'hotfix/1.0-hotfix-1' has been merged into 'master'
- The hotfix was tagged '1.0-hotfix-1'
- Hotfix tag '1.0-hotfix-1' has been back-merged into 'develop'
- Hotfix branch 'hotfix/1.0-hotfix-1' has been locally deleted
- 'develop', 'master' and tags have been pushed to 'origin'
- You are now on branch 'develop'

As we can see the process is very similar to release. The only difference is the originating branch which was master in this case.

Working with pull requests

It’s a good practice to create pull requests and ask colleagues to review the code before merging into develop. This can still be part of Git Flow.

In this example I’ve created a new feature called “customers”. The changes are represented by the file “Implemented Customers Feature in This File.md”. I’ve commited my code to my feature branch and pushed it to GitHub.

Now I can click a new Pull Request by clicking Compare & pull request button:

I’ve clicked on Create Pull Request and now it’s open on GitHub.

At this point there are several strategies you can take such as reviewed handling the merge and the developer deleting the local branch themselves (as there won’t be a Finish Feature step).

My suggestion is for the reviewer to Approve the pull request but not to merge it. Once the PR is approved, the developer receives a notification. Also the status of the PR can be seen on GitHub as well:

At this point the developer can go back to Git Flow and finish feature as shown previously.

Now that we know customers feature have been approved we can select that and finish that feature:

Summary of actions:
- The feature branch 'feature/customers' was merged into 'develop'
- Feature branch 'feature/customers' has been locally deleted; it has been remotely deleted from 'origin'
- You are now on branch 'develop'

At this point developer needs to push the merged develop branch to GitHub otherwise the recent change (approved customers change) doesn’t appear in remote even though the pull request was merged.

Even if you’re in the middle of another feature and have uncommited files you can still finish the approved feature so it doesn’t interrupt with your work.

Command Line

All the Git Flow commands explained above are also supported by command line but to be able to use them you have to install it:

brew install git-flow

Then you can start using it as

git flow <subcommand>

In Windows, command line tools come with the installer so no extra installation is necessary.

Resources

hobby raspberry_pi, productivity

The Problem

Snoozing is the worst possible thing you can do to yourself! You start the day with a feeling of guilt and being late for everything from the start. To avoid this I’ve been working on improving my toolkit and techniques. I posted this as a list of my current methods and tools.

Whilst these methods worked for me a while I’ve started having an issue with Alexa playing my playlist from Spotify: It always starts with the same song. My idea was to have a different song every morning so this bug in Spotify impeded me from achieving my goal.

The Idea

As the saying goes: If all you have is a hammer, everything looks like a nail!

Even though I have other means of developing tools and software, I wanted to use another Raspberry Pi to replace Alexa morning routine (it still turns on the light though).

The Execution

So I decided to dust off my OG Raspberry Pi 1 Model B and implement my own custom solution for playing a random song every morning to help wake me up.

Here’s how I implemented it from start to finish:

Step 01: Download Raspbian image and prepare SD/MicroSD card

I downloaded Raspbian Buster Lite as I don’t need desktop and other extra software for this project.

I followed the official guide from raspberrypi.org to burn the image. It’s basically just a few commands:

Insert the card and run the following to identify the card:

diskutil list

Then

sudo dd bs=1m if=path_of_your_image.img of=/dev/rdiskn conv=sync

where n is the number of disk which I found from the first step.

If you get “resource is busy” error, then run the following

diskutil unmountDisk /dev/diskn

Step 02: Carry Out Necessary Configuration Updates

It’s a good practice to change the default passwords so I ran

passwd

and updated the password.

Also for remote installations I enabled SSH:

sudo systemctl enable ssh
sudo systemctl start ssh

And I like to give a specific name to all my devices so that I can keep an eye on the machines connected to my network more easily.

To achieve that I ran:

sudo hostname NEW_HOSTNAME

I used raspi-config to connect to WiFi network

sudo raspi-config

and enter the details and rebooted to connect to the network.

In this MVP implementation I don’t even need Internet connectivity (after the initial installations below) as all my MP3 files are on the device already. But in my tests I noticed that when the Pi is disconnected the system time wasn’t updating so scheduled job wasn’t being triggered. This is probably because I’m using a Pi 1. I’m guessing the newer version can work offline as well.

Step 03: Install MP3 Player Software

In this simple project I’m going to install mpg123 and run it via a Python script

Installation is as simple as this:

sudo apt-get install mpg123 -y

Step 04: Develop the script

The following script points to a folder which supposedly have all the local MP3 files, picks one randomly and starts playing.

import os
import random

path ='/path/to/mp3/folder' 
files = os.listdir(path)
index = random.randrange(0, len(files))

os.system(f'/usr/bin/mpg123 {files[index]}')

Step 05: Copy music and script to Raspberry Pi

Save the script above in a USB drive along with the MP3 files. To copy the files from USB drive to Pi microSD card, I inserted my USB and ran the following:

sudo fdisk -l
sudo mkdir /media/usb
sudo mount /dev/sda1 /media/usb

Then

mkdir /home/pi/music
cp /media/usb/*.mp3 /home/pi/music

and copy the script to home folder

cp /media/usb/alarm-pi.py /home/pi

Step 06: Schedule the script

crontab -e

Then add to run at a certain time. The example below runs at 7:30am everyday:

30 7 * * * /usr/bin/python3 /home/pi/alarm-pi.py

If you want to run the script multiple times you can another one (like another one after 10 minutes as a snooze-prevention mechanism!)

Step 07: Test the schedule

Update your schedule temporarily and set it to a few minutes later and start playing the waiting game! Once you hear a random song playing make sure to set it back to your actual alarm time.

Conclusion

I meant to run this in a Docker container and develop the application with dotnet core but turns out playing MP3 with dotnet core in Linux is not easy. Also playing sound from inside a container has its own issues. So for those reasons I chose the easiest possible path to achieve the goal. If I find time I’ll improve this and make it more flexible but for the time being it does the job so I’ll assume MVP is done now.

Resources

security pi_hole, raspberry_pi

For some time I’d been wanting to set up my own Pi Hole in my network.

In this previous post I implemented a Docker registry on Raspberry Pi. I was planning to install it using Docker on that Pi but then I decided to set up a new Pi from scratch due to performance concerns. After I get the hang of it I might migrate it to Docker.

Preparations: Download Raspbian image and prepare Micro SD card

As of this writing, the latest Raspbian release is Buster but in the official supported OS list the latest supported Raspbian is Stretch. So I went to this page to download Raspbian Stretch Lite.

I downloaded Raspbian Buster Lite as I don’t need desktop and other extra software for this project.I followed the official guide from raspberrypi.org to burn the image. It’s basically just a few commands:

I inserted the card and run the following to identify the card:

diskutil list

Then

sudo dd bs=1m if=path_of_your_image.img of=/dev/rdiskn conv=sync

where n is the number of disk which I found from the first step.

If you get “resource is busy” error, then run the following

diskutil unmountDisk /dev/diskn

Carry Out Necessary Configuration Updates

It’s a good practice to change the default passwords so I ran

passwd

and updated the password.

Also for remote installations I enabled SSH:

sudo systemctl enable ssh
sudo systemctl start ssh

And I like to give a specific name to all my devices so that I can keep an eye on the machines connected to my network more easily.

To achieve that I ran:

sudo hostname NEW_HOSTNAME

No need to set a static IP address at this point as Pi Hole setup takes care of that:

Install Pi-Hole

There is an easy one-step installation:

curl -sSL https://install.pi-hole.net | bash

I changed my IP to a different static IP and rebooted the Pi.

After that I could go to the admin page via http://{Pi Hole Static IP}/admin/ and see the dashboard:

Test

Now it’s time to see if it’s working at all. I have 2 MacBooks connected to the same network. I changed one of the laptop’s DNS server to point to Pi Hole and left the other one intact.

In the laptop that’s still using the old DNS settings, I opened a Chrome window in incognito mode and visited a site and at top it shows an ad:

And on the other laptop I repeated the process and I got the same page without ads this time:

When I go to the admin page I can see what calls are blocked:

Also a nice thing about it you can choose to Whitelist certain providers. For instance in the image below it looks like Google Analytics is blocked and I can whitelist it easily:

Resources