Howto Rename Git Master Branch

While Git uses the term master in the sense of “master record”, it’s clear that master as a term has connotations to master-slave methaphor which is both technically and historically inaccurate according to The Internet Engineering Task Force. Here’s how to rename Git master branch both in your local repository and on GitLab.

1. Rename Git Master Branch

First, rename master and push the change to origin, then update local references to this new name. (I’m using trunk here as the new name but you can use main or whatever you wish. Just change the commands accordingly.)

git checkout master git branch -m master trunk git push -u origin trunk  git branch --unset-upstream git branch -u origin/trunk git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/trunk

2. Change GitLab Default Branch (And Delete master)

Navigate to Settings > Repository and set the new default branch. (In GitHub you’ll find the same setting under repository Settings > Branches.)

Settings

Lastly you may want to delete the old master branch but you need to remove protected branch status from GitLab before being able to do that. Go to Protected Branches on this same Settings > Repository page and Unprotect the master branch if it’s protected. (Don’t forget to add the same protections back to the new branch!) Now we can delete the old master branch:

git push origin --delete master

Bonus: Change Default Git Branch For New Repositories

Since Git version 2.28 you can easily set the default branch name for all new repos with a simple command:

git config --global init.defaultBranch trunk

Congrats for participating in making the word just a little bit better place for all of us!

Howto Add TypeScript to Your Existing Vue Project

The more I work with scripting languages like JavaScript with modern tools like VS Code, the more I learn to appreciate the powers of static typing. TypeScript is a typed superset of JavaScript that compiles to plain JavaScript, and with good tools it can seriously improve your productivity.

Vue has a somewhat mixed TS story. On the other hand most notable packages support it well but then there are also large painpoints like Vuex that can make your life miserable. Upcoming Vue 3 is completely rewritten in TypeScript which will improve TypeScript support hugely. In attempt to future-proof a rewrite of a large existing Vue app, I decided to start gradually typing it with TypeScript. Here are some of the initial notes from my experience.

(Disclaimer: even though I’m an experienced Vue developer, I’m a complete TS-newbie so YMMV.)

The Unbearable Lightness of Starting Slow

Problem with most TypeScript tutorials and simple examples is that they expect you to either start from scratch or completely convert all of your code to TypeScript from the very first step. For an existing codebase, that’s obviously not going to happen. I would also personally advice against it even if there were tons of tests and all the time in the world to do it, especially if you’re new to TypeScript.

The great secret of TypeScript is that you can start as small as you like and add typings gradually. By doing it this way you’ll probably find some problems with your existing code and get the chance to refactor it properly as you move forward. Granted, you won’t get the full benefits of TypeScript before you reach a certain critical point of typing completeness, but starting it small will cost you almost nothing so its very easy to get started. Life with legacy codebases is all about compromises and this path will at least give you a chance for better future.

Step 1: Initial Installation

If you’re vue project is created with vue-cli, adding typescript looks easy: just vue add typescript. Problem is, the defaults may not be that good for you and you should know what they mean. Here’s what I answered to the installer questions:

Use class-style component syntax? ❌No

By default this plugin suggests you to use class-style components which are an easy way to get started with Vue and TypeScript. Class based api was supposed to be the future of Vue, but the class syntax is in many ways problematic and if you plan to upgrate to Vue 3, you will want to consider composition api instead.

Note that many tutorials and existing TypeScript code for Vue uses class syntax. If you opt out here you have much less refactoring to do with your JavaScript and you are more future-proof with Vue 3, but you will also lose a big chunk of the examples and tutorials out there right now.

Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? ✓ Yes

If you have an existing project where you use Babel, you’ll definitely want to keep using it with TypeScript as well.

Convert all .js files to .ts? ❌No

This is the most problematic suggestion of them all. Sure, if you’re converting a small app with five files for fun, do it. But if you’re adding TypeScript to a larger project, definitely do not rename all of your JavaScript files in one go, that’s just asking for trouble.

Allow .js files to be compiled? ❌No

As the documentation on the whole plugin is very thin, I don’t exactly know what this option even means but I assume it’s one more toggle to help you bruteforce TypeScript into your project. By default any JavaScript code will stay JavaScript and only .ts files and script tags with lang="ts" will be compiled, and that’s the way you’ll want it to be when starting gradually.

Step 2: Cleaning Up

The “helpful” installer will modify your existing code so you’ll need to do a bit of cleanup before getting back to business. First, you’ll want to delete the re-appeared src/components/HelloWorld.vue as I’m quite sure no one is actually using that in a real project (right??). You also want to fix the havoc in your src/App.vue while you’re at it. The main.js is now renamed to main.ts. This is a good change as it reminds you immediately that this project uses TypeScript even though most of the files might still be JavaScript.

The newly added tsconfig.json includes somewhat strict settings that may or may not work with your project. The stricter the rules, the more benefits you get from TypeScript, but on the other hand the stricter the rules, the harder it is to make your existing code valid. For me one of the first steps with an existing project was to disable strict checking. This allowed me to gradually add typings to complex files without spending hours and hours in debugging or adding the file full of ignores. Note that by loosening the checking you will lose some of the most powerful gains you could have from your editor and TS in general so evaluate yourself if you can leave it on or not.

(For example, with strict checking on, when editing a method inside a Vue component, the editor will know that this refers to a specific Vue instance. This is super powerful when it works. When strict checking is disabled, this inside of a component method is inferred as any type which will not help you at all.)

I would not tweak tsconfig too much more before you understand better the challenges in your specific project. Give yourself time to test and experiment before committing to any specific rule.

Step 3: Start Adding Typings

Now you are ready to start adding your first types! I’d suggest to start in a file that is large or otherwise not so easy to read at a first glance — this is where you’ll get most gains.

This is not a tutorial on TypeScript itself, you’ll find plenty of them elsewhere. The migrating from JavaScript document is a good read and Using TypeScript with Vue Single File Components was very helpful to me as well. Vue + TypeScript Cookbook is a brilliant source of practical advice and it also points out some of the long standing issues such as the problems with Vuex.

Oh yes, Vuex. Have a plan for Vuex. Almost all larger projects use Vuex and for example the mapState helper, so you’ll immediately be in a world of trouble as Vuex does not have proper TypeScript support and no simple solution has been found by the community. This does not mean that you can’t use Vuex with TypeScript or that there are no solutions. It just means that there are no good universal solutions. You need to experiment and evaluate which of the dozens of proposed workarounds works best for you. And know that if it at some point stops working in your specific edge case, you’re alone with your problem as there is no officially supported way. This obviously sucks big time, but there is at least some light at the end of this tunnel as Vue 3 will eventually bring proper TypeScript support to Vuex as well.

Try not to ignore code. Alarm bells should ring if your TypeScript code starts gathering // @ts-ignore-lines more than actual refactoring. It’s the same with linting: the point is to make your code better, not to work around your tooling. Sure, there are situations where practicality beats purity but make a mental note for yourself to keep those situations exceptions. If you find a file too hard to convert, just leave it be until you or your tools become smarter.

There are a couple of good rules of thumb you should keep in mind. First, all new code should be typed. This is kind of obvious but also easy to forget. Add documentation and processes in place to make sure that from now on, all new code in the project is written in TypeScript instead of JavaScript. You can help this process with CI scripts and other tools: ban adding .js files and if you have tools that generate new files, convert them to generate .ts.

Secondly, try not to change too much in one go. It’s very easy to stumble down into the rabbit hole and use hours and hours adding typings with very little practical gains. As you learn more and as Vue world matures, you’ll be getting more and more out of TypeScript. And then some day when you understand more about your project and the tools, you’ll see more clearly when it is the right time to take the last step of converting all the remaining code to TypeScript. Remember, cleaning up your codebase is not a race, it’s a neverending journey.

Things Will Break — It’s a Good Thing

The thing with TypeScript that makes it both helpful and painful at the same time is the fact that it’s much more unforgiving to mistakes that you’ve used to with JavaScript. My very first steps with TS in a real world project involved several hours of debugging a file that would not compile no matter what I did. Turns out it was due to a missing import in a legacy method that was never called. You’ll never find out things like this in JavaScript but they will bite you with TypeScript as soon as you turn it on, so be mentally prepared.

Legacy code is difficult in many ways and hunting down new bugs that previously weren’t there can feel frustrating. But remember that this is exactly why you want to use TypeScript in the first place: to see and fix those bugs early. It’s very likely that your old production code is relying on a bug you didn’t even know was there and turning on TypeScript will now break that code. Learn to embrace these growing pains because fixing them will make your code better and keep your productivity high in the long run.

Howto: Draft Posts With Gridsome

Gridsome is a Vue-based static site generator inspired by Gatsby. As of version 0.7 there is no official way to have a publishing flow that separates draft and published posts. For me this is a big deal as I like to iterate on my writings for several days or weeks before publishing them. Luckily, there is an easy way to add support for draft posts yourself.

Adding a New GraphQL Property

At the heart of a Gridsome site is a GraphQL data layer that you can use and manipulate in development mode. Gridsome fetches any sources you define into this GraphQL storage in development mode and during the build process writes the data into static files as instructed in your configuration.

The naive way of importing any data into Gridsome just adds it into the storage and it then respectively gets published as you build the site. A super simple way to implement draft functionality is to simply add an attribute to your data object and then filtering by this attribute when building the site.

Add the following javascript in a file called gridsome.server.js in the root of your Gridsome project:

module.exports = function(api) {   api.loadSource(store => {     if (process.env.NODE_ENV === 'production') {       const posts = store.getContentType('Post')        posts.data().forEach(node => {         if (node.published !== true) {           posts.removeNode(node.id)         }       })     }   }) }

(Change the Post ContentType to match whatever your source plugin typeName has been configured to in gridsome.configjs plugins section. The default for source-filesystem-plugin is FileNode.)

Now, when you run gridsome build, the API that loads your sources at build time filters out each post that hasn’t got a published-attribute that evaluates to true. (Note that if you have a blog with lots of content and you don’t want to add a new attribute to all of them, you might want to reverse the logic, for example to if (node.draft !== true). I like to explicitly mark published posts, YMMV.)

For Markdown and Front Matter content you can now add published: false to your post meta to be able to filter out draft posts in production. (Modifying other content sources is left to the reader.)

Building Site Search And OpenSearch-plugin With Django

While browsing trough Technorati I saw this blue glow on the Firefox search bar. Firefox had autodiscovered an opensearch-plugin for Technorati. How could I implement this functionality on my own site? Turns out this is ridiculously easy to do with Django. The result for this blog looks like this:

Again, it’s very easy to do. (I know, because I could do it! 🙂

Start with search

First, you need to have a search function for your site. Unfortunately Django doesn’t have any generic search functionality built in (yet, search-api is on its way). There is however, very nice search functionality in Admin-app, which can be easily modified to a site search.

After a bit of tweaking, this is the view code that I eventually came up with:

 from django.shortcuts import render_to_response from django.db.models import Q from django.db.models.query import QuerySet from django.http import HttpResponseRedirect from django.template import loader, Context from unessanet.english.models import BlogEntry import operator   def hoyci_search(request, terms=None):     if request.POST:         return HttpResponseRedirect('/en/hoyci/search/%s/' % request.POST['query'].replace(" ","+"))     elif terms:         query = terms.replace("+"," ")         if query:             or_query=Q()             search_fields = ['title', 'strapline', 'excerpt', 'body'] # your search fields here              for bit in query.split():                 or_queries = [Q(**{'%s__icontains' % field_name: bit}) for field_name in search_fields]                 other_qs = QuerySet(BlogEntry) # your class here                 other_qs = other_qs.filter(reduce(operator.or_, or_queries))                 search_results = other_qs.filter(is_draft=False)         else:             search_results = None         return render_to_response('english/search.html', locals())     else:         return render_to_response('english/search.html')

Let’s chew it up a bit. Firstly, in my urls.py I have a line

(r'^search/(?P<terms>\S+)/$', 'hoyci_search'),

which passes the terms variable to the search function. When the function is called, it checks whether the request was POST or GET. Searches via web-form are done with POST, while other searches (like Firefoxes opensearch) are GET. If request is POST, it redirects it to a new URL, which is also the result page.

All the heavy lifting is done when prosessing the GET-request. If you want to use this code, modify the search_fields-variable and the first instance of other_qs-variable to set up right model class and its fields. After that (and after spitting up the necessary template files), everything should just work! You don’t have to worry for example about SQL-injections because Django db-api takes care of things like that for you. How convenient 🙂

(The locals()-trick was found from the Django Book, btw.)

Implementing OpenSearch With Django

Mozilla developer center has a good article about creating OpenSearch plugins for Firefox. Basically, the “plugin” is just an XML-file and a rel-link to notify the browser about it. I basically copied the example XML to a Django template and filled in the data. My description file looks like this:

<OpenSearchDescription   xmlns="http://a9.com/-/spec/opensearch/1.1/"   xmlns:moz="http://www.mozilla.org/2006/browser/search/" >   <ShortName>Same Con To Hoyci</ShortName>   <Description>Search entries from Hoyci</Description>   <InputEncoding>UTF-8</InputEncoding>   <image width="16" height="16" type="image/png"     >http://kuvat.unessa.net/stuff/unessa_ulogo_16px.png</image   >   <Url     type="text/html"     method="GET"     template="http://www.unessa.net/en/hoyci/search/{searchTerms}"   ></Url>   <moz:SearchForm     >http://www.unessa.net/en/hoyci/search/{searchTerms}</moz:SearchForm   >   <Language>en-us</Language> </OpenSearchDescription>

This file needs to be fed to the browser, so we need another line in urlconf and another view. The easiest way to do it would be to call django.views.generic.simple.direct_to_template, but because Django is for perfectionists, we’ll do it with a custom view to get right mime-type. Like this:

 def hoyci_opensearch(request):     response = HttpResponse(mimetype='application/opensearchdescription+xml')     t = loader.get_template('english/hoyci_opensearchxml.html')     response.write(t.render(''))     return response

Quite painless. Now the only thing missing is the autodiscovery link to the template. It looks like this: <link rel="search" type="application/opensearchdescription+xml" title="Hoyci Search" href="http://www.unessa.net/en/hoyci/opensearch/">. And that’s it. Now you can search your site from Firefox search bar!

Final notes

This was yet another “hack together something interesting in half an hour”-type of hack. It’s not 100% optimized or fully tested, but for me it works like a charm.

The search functionality has some known limitations (aka “features”):

  • It only searches from one class. It would be nice to be able to search multiple classes.
  • Due to URL-limitations, my solution breaks with Firefox with characters like ‘/’, because Firefox encodes them and Django doesn’t seem to comprehend it. Django chokes for example on “foo%2Fbar”, which is not nice.

The Firefox OpenSearch implementation has a nice suggestion feature. It would be quite easy to built one with Django using a simple searches-class and json output to the browser. Maybe a sequal to this howto?-)

Any tips on the limitations, and any other comments and suggestions, are appreciated!

Highlighting Code Using Pygments and Beautiful Soup

Syntax highlighting in blog posts is something that has always bugged me. I don’t like JavaScript-based solutions so I wrote a quick&dirty function that highlights Python-code in my blog posts on the server side. Following examples are written for Django, but they should work on any Python software.

The problem

I want to use Markdown and still be able to have automatic syntax highlighting for Python code that’s inline in my blog posts. Markdown alone tends to break HTML-formatted source code (because of indentations, etc) so fully working solution needs a bit tweaking.

The Solution

We’ll need:

With these tools we’re able to build a helper function that looks for source-code in a given text, highlights it’s syntax and applies Markdown filtering to it without messing up the syntax highlighted code.

The Code

My (simplified) BlogEntry model looks like this:

 class BlogEntry(models.Model):     title = models.CharField(maxlength=500)     body = models.TextField(         help_text='Use <a href="http://daringfireball.net/projects/markdown/syntax">Markdown-syntax</a>')     body_html = models.TextField(blank=True, null=True)     pub_date = models.DateTimeField(default = datetime.datetime.now)     use_markdown = models.BooleanField(default=True)      class Admin:         fields = (             (None, {                 'fields' : ('title', 'body', 'pub_date', 'use_markdown')             }),         ) 

Redundant body_html element is for performance: instead of calculating markdown- and syntax highlight for the body on every request, we calculate it only on every save. (Yes, it could also be done on the body-field itself, but I prefer that the content I’m editing does not change every time I save it.)

Next the highlighting function:

     def _highlight_python_code(self):         from pygments import highlight         from pygments.lexers import PythonLexer         from pygments.formatters import HtmlFormatter         from unessanet.misc.BeautifulSoup import BeautifulSoup          soup = BeautifulSoup(self.body)         python_code = soup.findAll("code", "python")          if self.use_markdown:             import markdown              index = 0             for code in python_code:                 code.replaceWith('<p class="python_mark">mark %i</p>' % index)                 index = index+1              markdowned = markdown.markdown(str(soup))             soup = BeautifulSoup(markdowned)             markdowned_code = soup.findAll("p", "python_mark")              index = 0             for code in markdowned_code:                 code.replaceWith(highlight(python_code[index].renderContents(), PythonLexer(), HtmlFormatter()))                 index = index+1         else:             for code in python_code:                 code.replaceWith(highlight(code.string, PythonLexer(), HtmlFormatter()))          return str(soup)

This function searches <code>-blocks that have class="python" attribute. It first replaces them with placeholder text, then applies markdown if necessary, and finally replaces the placeholders with syntax highlighted code. It may not be the most beautiful code, but it works 🙂

And finally the save method:

 def save(self):     self.body_html = self._highlight_python_code()     super(BlogEntry,self).save()

The body_html-field is updated on every save. On the template side you can use simply {{ entry.body_html }} without applying any additional filters.

The CSS needed for syntax coloring can pe printed out with Pygments for example like this: css = HtmlFormatter().get_style_defs('.highlight'). It may be wise to save the code and put it in a static CSS-file.

Known Limitations

  • Not a bug, but feature, every instance of code-tags that have class="python" will be replaced. This was a bit annoying when trying to document this particular function…
  • Unicode strings break the highlighter. Any help on this is appreciated!

This code is published under Creative Commons License. Please share any comments! 🙂