Braindump Versions, Release Notes, and the Road Ahead

I have been playing with a couple different methods of versioning and release notes with Braindump. I used to do all release notes in GitHub. GitHub “Releases” are nothing more than git tags with some additional meta-data and after talking to eric one day I agree that locking myself into GitHub specific tags is not the best idea. Then I started to add release notes on my blog and just link to it from the GitHub tag, this worked ok but you may have noticed that I blogged about version 0.3.0 yesterday and then released versions 0.3.1, 0.3.2, 0.3.3, and 0.3.4 today. I think I finally came up with a solution that makes the most sense to me.

Braindump is using semver, so the scheme is MAJOR.MINOR.PATCH. So going forward, I will create release notes only for MAJOR and MINOR versions.

With the new Docker deployments I am creating two types of containers.

  1. latest which refers to the latest commit on master
  2. stable which refers to the latest git tag

I also have created some milestones and put every current issue into a milestone. Patch versions are now milestones and they will come and go quickly. Major and Minor versions are going to be more long running milestones. The Ice Box is where dreams to go ~die~ come true some day.

Braindump.pw will always be running the latest patch version, or stable. I hope this new methodology will provide some better structure and clarity for this project.

Recreating Foreign Keys with Alembic

Alembic is a great tool for keeping track of schema changes in python applications. I am using it to manage DB migrations for braindump along with Flask SQL Alchemy as my ORM. One challenge is managing proper foreign key constraints. By default if you define a foreign key relationship in your schema definition it will not generate the proper migration code. For example, in braindump we have a one to many relationship between users and notes.

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(254), unique=True, index=True)
    password_hash = db.Column(db.String(256))
    confirmed = db.Column(db.Boolean, default=False)
    avatar_hash = db.Column(db.String(32))
    created_date = db.Column(db.DateTime(), default=datetime.utcnow)
    updated_date = db.Column(db.DateTime(), default=datetime.utcnow)

    notes = db.relationship(
        'Note', backref='author',
        lazy='dynamic', cascade="all, delete-orphan")

Even though we define the cascade behavior using SQLAlchemy. When we generate the migration with alembic we get something like this:

sa.ForeignKeyConstraint(['author_id'], ['users.id'], ),

Notice how we are missing the ondelete action. What we actually want is something like this:

sa.ForeignKeyConstraint(['author_id'], ['users.id'], ondelete='CASCADE')

Running the default migration will not create the proper relationship in your database and in our case we are not able to delete a user until we have deleted all of the related records as well. There are two ways to fix this. If you catch this before running your migration addingondelete='CASCADE' will create the proper relationship. If you are like me, and do not catch this, then you will need to run a second migration to remove and recreate these keys. The migration code to do this is shown below:

from alembic import op
import sqlalchemy as sa

def upgrade():
    with op.batch_alter_table("notes") as batch_op:
        batch_op.drop_constraint(
            "notes_author_id_fkey", type_="foreignkey")
    op.create_foreign_key(
        "notes_author_id_fkey", "notes", "users",
        ["author_id"], ["id"], ondelete="CASCADE")

Now you have the proper foreign key constraints and the CASCADE action exists in the DB.

Version 0.3.0 of Braindump Released

I am excited to announce the release of version 0.3.0 of Braindump. This release contains few new features, but some pretty big changes have happened with the way that Braindump is being deployed.

New Features

  • Switched to using Prose Mirror instead of ACE. I have been super impressed with Prose Mirror and I think that it will make the writing experience on Braindump much better.

UX

  • New Logo (thank you Songbing :))

Ops and Dev

  • Switched to NPM for Javascript Dependency Management
  • Completely Dockerized the application using Docker Compose, CircleCI and Quay.io, each git tag is now being automatically deployed to production while each master build is building an image.
  • Completely Switched to Python 3 (3.5 to be exact). Props to Ricardo for always trolling me about living in the past.

Twitter Has Become Unbearable

I feel that lately Twitter has just become unbearable. I am going to sound like a curmudgeon but I miss the old twitter. Remember when you had a timeline full of peoples ideas? Remember when more than 3 tweet fit on a page? Remember when you used to be able to be able to see tweet from people that you actually follow? Those were the good old days, when I log into Twitter these days I am bombarded with bullshit.

But Lev, why not just stop using Twitter instead of complaining about it on your blog?

That is an excellent question.

Believe it or not, I actually like Twitter. The people I chose to follow consistently provide me with awesome things to read about, explore and discuss. The problem is that finding the things that I care about has become increasingly difficult in this sea of bullshit.

I thought all hope was lost until I found Twipster. It seems like it was made specifically for curmudgeon’s like me. I absolutely love this extension, it just cuts out all of the bullshit and makes Twitter usable for me again. I made a few additions to also remove images and videos and personally I think this is a much better twitter experience.