diff --git a/release/generate_changelog.py b/release/generate_changelog.py
index 52c5c425659995c17dff723ae8246b8089b9b46b..6472df19b049c78d83bfcbe09e6fda334f5dff56 100755
--- a/release/generate_changelog.py
+++ b/release/generate_changelog.py
@@ -10,7 +10,7 @@ Usage:
 
 Arguments:
   <private_token>  Private token used to access GitLab.
-  
+
 Options:
   -h --help                     Show this screen.
   --version                     Show version.
@@ -21,6 +21,7 @@ Options:
   -l, --list-of-packages STR    A file with the list of packages (in order) to generate changelog for.
                                 By default, it will look in file 'order.txt' inside 'bob.nightlies' package.
                                 [default: bob.nightlies].
+  -R, --release-mode            If provided, it will only show the changelog of tags.
 """
 
 import sys
@@ -89,11 +90,11 @@ def print_tags(pkg_name, gitpkg, since='2017-01-01T00:00:00Z'):
 def print_one_tag(pkg_name, tag):
     print('  * ' + tag.name + ' (' + get_datetime_from_gitdate(tag.commit['committed_date']).strftime(
         '%b %d, %Y %H:%M') + ')')
-    for line in get_tag_changelog(tag).split('\r\n'):
+    for line in get_tag_changelog(tag).replace('\r\n', '\n').split('\n'):
         line = line.strip()
         if line.startswith('* ') or line.startswith('- '):
             line = line[2:]
-        line = line.replace('!', pkg_name + '!')
+        line = line.replace('!', pkg_name + '!').replace(pkg_name+pkg_name, pkg_name)
         line = line.replace('#', pkg_name + '#')
         if not line:
             continue
@@ -118,13 +119,13 @@ def print_commits_range(pkg_name, commits):
                         'Increased stable' in commit_title:
             continue
         commit_title = commit_title.strip()
-        commit_title = commit_title.replace('!', pkg_name + '!')
+        commit_title = commit_title.replace('!', pkg_name + '!').replace(pkg_name+pkg_name, pkg_name)
         commit_title = commit_title.replace('#', pkg_name + '#')
         # print('  - ' + get_datetime_from_gitdate(x.committed_date).strftime('%Y-%m-%d %H:%M:%S') + ":" + commit_title)
         print(' ' * 5 + '- ' + commit_title)
 
 
-def print_tags_with_commits(pkg_name, gitpkg, since='2017-01-01T00:00:00Z'):
+def print_tags_with_commits(pkg_name, gitpkg, since='2017-01-01T00:00:00Z', release_mode=False):
     # get tags since release and sort them
     datetime_since = get_datetime_from_gitdate(since)
     tags = gitpkg.tags.list()
@@ -146,27 +147,34 @@ def print_tags_with_commits(pkg_name, gitpkg, since='2017-01-01T00:00:00Z'):
         commits4tag = filter(lambda x: (
             get_datetime_from_gitdate(x.committed_date) > start_date and get_datetime_from_gitdate(
                 x.committed_date) <= end_date), commits)
-        print_commits_range(pkg_name, commits4tag)
+        if not release_mode:
+            print_commits_range(pkg_name, commits4tag)
         start_date = end_date
-    # print the tentative patch version bump for the future tag
-    print('  * patch')
-    # print leftover commits that were not tagged yet
-    leftover_commits = filter(lambda x: get_datetime_from_gitdate(x.committed_date) > start_date, commits)
-    print_commits_range(pkg_name, leftover_commits)
+    if not release_mode:
+        # print the tentative patch version bump for the future tag
+        print('  * patch')
+        # print leftover commits that were not tagged yet
+        leftover_commits = filter(lambda x: get_datetime_from_gitdate(x.committed_date) > start_date, commits)
+        print_commits_range(pkg_name, leftover_commits)
 
 
-def main(private_token, group_name='bob', pkg_name="", packages_list='bob.nightlies'):
+def main(private_token, group_name='bob', pkg_name="", packages_list='bob.nightlies', release_mode=False):
     gl = gitlab.Gitlab('https://gitlab.idiap.ch', private_token=private_token, api_version=4)
     bob_group = gl.groups.list(search=group_name)[0]
     last_release_date = bob_last_release(gl)
 
+    if release_mode:
+        visibility = ('public', )
+    else:
+        visibility = ('public', 'private', 'internal')
+
     # print name of the package, its version and tags
     if pkg_name:
         # group returns a simplified description of the project
         grpkg = _insure_correct_package(bob_group.projects.list(search=pkg_name), group_name, pkg_name)
         # so, we need to retrieve the full info from GitLab using correct project id
         gitpkg = gl.projects.get(id=grpkg.id)
-        print_tags_with_commits(pkg_name, gitpkg, since=last_release_date)
+        print_tags_with_commits(pkg_name, gitpkg, since=last_release_date, release_mode=release_mode)
     else:
         if packages_list == 'bob.nightlies':
             pkg_list_ordered = get_packages_list(gl, gl_group=bob_group)
@@ -181,12 +189,15 @@ def main(private_token, group_name='bob', pkg_name="", packages_list='bob.nightl
             grpkg = _insure_correct_package(bob_group.projects.list(search=pkg_name), group_name, pkg_name)
             # so, we need to retrieve the full info from GitLab using correct project id
             gitpkg = gl.projects.get(id=grpkg.id)
+            if gitpkg.attributes['visibility'] not in visibility:
+                continue
             #        print_tags(pkg_name, gitpkg, since=last_release_date))
             #        print_commits(pkg_name, gitpkg, since=last_release_date)
-            print_tags_with_commits(pkg_name, gitpkg, since=last_release_date)
+            print_tags_with_commits(pkg_name, gitpkg, since=last_release_date, release_mode=release_mode)
 
 
 if __name__ == '__main__':
     arguments = docopt(__doc__.format(sys.argv[0]), version='Changelog 0.0.1')
     main(arguments['<private_token>'], group_name=arguments['--group-name'],
-         pkg_name=arguments['--package-name'], packages_list=arguments['--list-of-packages'])
+         pkg_name=arguments['--package-name'], packages_list=arguments['--list-of-packages'],
+         release_mode=bool(arguments['--release-mode']))
diff --git a/release/release_bob.py b/release/release_bob.py
index 647e598fcf32152a51fff8cb141a4bb5e6bec3b0..e8be6be910beb03eebf3cb437fee9dc8c90d5efd 100755
--- a/release/release_bob.py
+++ b/release/release_bob.py
@@ -415,6 +415,48 @@ def parse_and_process_package_changelog(gl, bob_group, pkg_name, package_changel
     return gitpkg, cur_tag, cur_tag_comments
 
 
+def release_bob(gl, bob_group, changelog_file):
+    instructions = '''
+    * Run the generate_changelog script first:
+        $ python generate_changelog.py -l ../../bob/requirements.txt -R -- TOKEN | tee bob_changelog.md
+        $ python release_bob.py -p bob -c bob_changelog.md -- TOKEN
+    * Put the pinnings below in requirements.txt (like ``bob.buildout == 2.1.6``) and meta.yaml (like ``bob.buildout 2.1.6``)
+    * Update the badges and version.txt to point to this version of Bob.
+    * Commit, push and tag a new version manually.
+        $ echo 4.0.0 > version.txt
+        $ git commit -am "Increased stable version to 4.0.0"
+        $ git push
+        $ git tag v4.0.0
+        $ git push --tags
+    * Put bob_changelog.md inside bob's tag description.
+    * Revert bob's requirement.txt and meta.yaml (no pinning) and version.txt and README.rst back to master version.
+    * Commit and push like this:
+        $ echo 4.0.1b0 > version.txt
+        $ git commit -am "Increased latest version to 4.0.1b0"
+        $ git push
+    * Enjoy!
+    '''
+    print(instructions)
+    # get the list of bob's dependencies.
+    # Get their latest tags (since bob's last release) and the tag's changelog
+    with open(changelog_file) as f:
+        saw_a_new_package = True
+        latest_tag = None
+        latest_pkg = None
+        for line in f:
+            # if saw_a_new_package:
+            if line.startswith('*'):
+                pkg = line[2:].strip()
+                saw_a_new_package = True
+                print(latest_pkg, '==', latest_tag)
+                latest_pkg = pkg
+                continue
+            if line.startswith('  *'):
+                latest_tag = line.split()[1][1:]
+            saw_a_new_package = False
+        print(latest_pkg, '==', latest_tag)
+
+
 def main(private_token, group_name='bob', changelog_file='changelog.rst', dry_run=False, package=None, resume=False):
     """
     Main function that updates and releases packages according to the provided changelog file.
@@ -430,6 +472,10 @@ def main(private_token, group_name='bob', changelog_file='changelog.rst', dry_ru
     gl = gitlab.Gitlab('https://gitlab.idiap.ch', private_token=private_token, api_version=4)
     bob_group = gl.groups.list(search=group_name)[0]
 
+    if package == 'bob':
+        release_bob(gl, bob_group, changelog_file)
+        return
+
     # traverse all packages in the changelog, edit older tags with updated comments,
     # tag them with a suggested version, then try to release, and
     # wait until done to proceed to the next package