Skip to content

Blog

Using Scrumban in a distributed team

Note

While this document is not in draft mode it is definitely not complete...

Being flexible and efficient can make working in a distributed team a great experience; sometimes though, you have to be the agent of change to help your team get there. While trying to understand what Scrumban actually is, it seemed best to type it up myself to reinforce what I have learned so far.

Scrumban combines two leading Agile methodologies—Scrum and Kanban—into a single process for getting work done. But what is scrumban? And why should you consider its unique approach?

These are notes based on the following resources:

What is Scrumban?

The Scrumban methodology combines the best features of Scrum and Kanban into a hybrid project management framework. It uses Scrum's stable structure of sprints, standups, and retrospectives. Then it adds Kanban's visual workflow and work-in-progress limitations. The result is a truly flexible method for managing projects of any size.

Agile Ceremonies

An Agile ceremony is an event in the Agile process when your team meets to discuss what their next course of action is.

There are four major Agile ceremonies that happen during every sprint cycle. Before starting each ceremony, your team members should understand the purpose of each meeting and how it impacts the sprint.

The 4 Agile ceremonies

  1. The sprint planning meeting
  2. The daily stand-up meeting
  3. The sprint review meeting
  4. The sprint retrospective meeting

Intentionally timely

Timeboxed meetings

As mentioned in the stand-up meeting wikipedia page, the stand-up meetings are intentionally timeboxed:

The meetings are usually timeboxed to between 5 and 15 minutes, and take place with participants standing up to remind people to keep the meeting short and to-the-point.6

Keep the meeting short

The daily stand-up meeting (also known as a “daily scrum”, a “daily huddle”, “morning roll-call”, etc.) is simple to describe:

The whole team meets every day for a quick status update. We stand up to keep the meeting short.

That's it.

But this short definition does not really tell you the subtle details that distinguish an effective stand-up from a waste of time.

So how can you tell?

The answer to the question of an effective stand-up is addressed in the It's Not Just Standing Up: Patterns for Daily Standup Meetings article, which is linked from the stand-up meeting wikipedia page.

The Kanban board

GitHub projects offers one way to use a Kanban board, this is accomplished using custom views in your GitHub project, specifically the board layout, which is based on the status field.

Example status field settings

Here is an example of Status field settings for a GitHub project:

  • Triage: Items to be triaged
  • To do: Triaged items up for grabs. Please choose based on determined priorities.
  • In Progress: Items actively being worked on
  • Review Needed: PRs and issues that require review
  • Changes Requested: Reviewed PRs and issues with changes requested
  • Approved: Approved PRs that need to be merged, or approved items that need to be completed
  • Done: Items that have been completed

Here is what that would look like in the Status field settings page:

github-project-status-field-settings

Using GitHub project workflows to improve timeliness

Note

This section is incomplete and should be in a separate blog post. It is "to be continued"...

Dark mode in chromium browsers

If you use a chromium browser and love dark mode, then keep reading.

Setting dark mode for everything

If you love dark mode for everything... you can enable Auto Dark Mode for Web Contents by setting chrome://flags/#enable-force-dark. If that does not work, then go to chrome://flags/ and search for Dark Mode.

Referenced from How do I set Google Calendar to Dark Mode?

turn on the flag Force Dark Mode for Web Contents you can do this by going to chrome://flags/ and searching for it

Browser dark mode extensions

Reset a Cisco Catalyst Switch

Here is a summary of the commands needed to reset Cisco Catalyst Switches to factory defaults:

# enter configuration mode
enable
# remove the startup configuration file
write erase
# remove the VLAN configuration
delete flash:vlan.dat
# then reboot the switch
reload

If there are other files that need to be deleted, run the following before issuing the reload command:

show file information flash:?
delete flash:config.text.backup
delete flash:config.text.old
delete flash:*.old

Enable debug for verbose GitHub Actions

!!!? info

This post is incomplete and will be updated in the future.

If you are using composite actions that support their own verbose mode, you may find you only want to enable verbose mode when the GitHub runner is in debug mode. The variable we need to know is runner.debug, which is also stored as RUNNER_DEBUG.

Understanding when variables are available

In a GitHub workflow there are three different situations where different environmental variables are available, GitHub calls this "context". There is workflow context, job context, and step context. The runner.* and RUNNER_* variables are available in the STEP environmental context, but not in the workflow or job environmental context.

Example workflow using debug mode

What this means is that in order to find out what the runner.debug variable is set to, you must check for the variable in a step. Here is an example workflow that will run only when the GitHub runner is in debug mode:

name: Print information only in runner debug

on:
  push:
    branches:
      - 'main'
    # ignore changes to .md files and the entire .github directory
    paths-ignore:
      - '**.md'
      - '.github/**'

jobs:

  runner-debug:
    runs-on: ubuntu-latest
    name: Print info in runner debug mode
    steps:

      - name: GitHub Runner Debug Mode
        if: ${{ runner.debug == '1' }}
        # to do:  run this if either the verbose input is true or the runner.debug is true
        id: runner-debug-mode
        ## The 'runner.*' and 'RUNNER_*' variables are not available in the WORKFLOW env context or the top-level JOB context, but are available in the STEP env context
        shell: bash
        env:
            EVAL_GH_VAR_RUNNER_DEBUG_EQ0: ${{ runner.debug == '0' }}
            EVAL_GH_VAR_RUNNER_DEBUG_EQ1: ${{ runner.debug == '1' }}
            FOOBAR: ${{ runner.debug == '1' && 'foo' || 'bar' }}
            # https://github.com/actions/runner/issues/2204#issuecomment-1287947031
            # https://github.com/orgs/community/discussions/27627#discussioncomment-3302259
            GH_RUNNER_LOG: "${{ runner.debug == '1' && 'INFO' || 'ERROR' }}"
            GH_VAR_RUNNER_DEBUG1: ${{ runner.debug }}
            GH_VAR_RUNNER_DEBUG2: ${{ env.RUNNER_DEBUG }}
        run: |
            echo "::group::starting the 'print-runner-context' step... "
            echo ""
            echo "NOTE: The 'runner.*' and 'RUNNER_*' variables are not available in the WORKFLOW env context or the top-level JOB context, but are available in the STEP env context "
            echo ""
            echo "eval if the 'runner.debug' is set to either '0' or '1' "
            echo "     runner.debug equal 0:  ${EVAL_GH_VAR_RUNNER_DEBUG_EQ0} "
            echo "     runner.debug equal 1:  ${EVAL_GH_VAR_RUNNER_DEBUG_EQ1} "
            echo ""
            echo "set FOOBAR to 'foo' if 'runner.debug' is '1'; otherwise set FOOBAR to 'bar' "
            echo "    FOOBAR:  ${FOOBAR} "
            echo ""
            echo "set GH_RUNNER_LOG to 'INFO' if 'runner.debug' is '1'; otherwise set GH_RUNNER_LOG to 'ERROR' "
            echo "    GH_RUNNER_LOG:  ${GH_RUNNER_LOG} "
            echo ""
            echo "the values of 'runner.debug' and 'env.RUNNER_DEBUG': "
            echo "    GH_VAR_RUNNER_DEBUG1:  ${GH_VAR_RUNNER_DEBUG1} "
            echo "    GH_VAR_RUNNER_DEBUG2:  ${GH_VAR_RUNNER_DEBUG2} "
            echo ""
            echo "finishing the 'print-runner-context' step... "
            ##
            echo "::endgroup::"
        ## The 'runner.*' and 'RUNNER_*' variables are not available in the WORKFLOW env context or the top-level JOB context, but are available in the STEP env context

Setting up a blog

From https://squidfunk.github.io/mkdocs-material/plugins/blog/

Material for MkDocs makes it very easy to build a blog, either as a sidecar to your documentation or standalone. Focus on your content while the engine does all the heavy lifting, automatically generating archive and category indexes, post slugs, configurable pagination and more.

Check out our blog, which is created with the new built-in blog plugin!

Encouraging git hygine with commitlint

I added a GitHub Action that uses commitlint to my GitHub Actions Monorepo. The GitHub Action is based off of commitlint (commitlint GitHub) and has been added in an effort to encourage (enforce?) good git hygiene. Note: The original actions-ci workflow was added in the v0.1.12 release.

The workflow originated from the CI setup GitHub Actions section of the commitlint guides. The example workflow needed to be updated in order to run, but it should be working now.

The default commitlint configuration:

module.exports = {
    extends: [
        "@commitlint/config-conventional"
    ],
}

Enforcing good git hygiene

Part of ensuring proper commit messages (and pull requests) will help with automating releases. For example, the semantic release tool can be used in a GitHub action, via this semantic-release-action.

Here are some other write-ups on the topic:

  • https://www.vantage-ai.com/blog/how-to-enforce-good-pull-requests-on-github
  • https://hugooodias.medium.com/the-anatomy-of-a-perfect-pull-request-567382bb6067
Resources

commitlint guide links:

Actions that can be used with commitlint:

  • https://github.com/webiny/action-conventional-commits
  • https://github.com/wagoid/commitlint-github-action
  • https://github.com/commitizen/conventional-commit-types
  • https://github.com/amannn/action-semantic-pull-request
  • (deprecated) https://github.com/squash-commit-app/squash-commit-app
  • (deprecated) https://github.com/zeke/semantic-pull-requests

Examples with a semantic.yml file within a GitHub repo:

  • https://github.com/GoogleChrome/lighthouse-ci/blob/main/.github/semantic.yml
  • https://github.com/minecrafthome/minecrafthome/blob/master/semantic.yml
  • https://github.com/meltano/sdk/blob/main/.github/semantic.yml
  • https://github.com/vectordotdev/vector/blob/master/.github/semantic.yml

Here are links to other resources:

  • https://github.blog/changelog/2022-05-11-default-to-pr-titles-for-squash-merge-commit-messages/
  • https://semantic-release.gitbook.io/semantic-release/recipes/ci-configurations/github-actions
  • https://jamiewen00.medium.com/integrate-commitlint-to-your-repository-67d6524d0d24
  • https://ajcwebdev.com/semantic-github/

Git describe with git tags

Getting tags on a git repository

Use git describe --tags to get the tags.

Using git describe

Examples using git describe

Using git describe --tags:

git describe --tags
## output
v0.2.1-15-g7a82bbe

Using git describe

git describe
## output
v0.1.2-39-g7a82bbe

Using git describe --abbrev=0 --tags to get the tag from the current branch

git describe --abbrev=0 --tags
## output
v0.2.1

Using git describe with git rev-list to get tags across all branches, not just the current branch

git describe --tags `git rev-list --tags --max-count=1`
## output
v0.2.1

Other examples
Other git describe examples (click to expand) ### Other examples using git describe Get the latest tagged version and remove the `v` prefix:
git tag --sort=committerdate | grep -E '[0-9]' | tail -1 | cut -b 2-7
## output
0.2.1
Using `git describe` with `--abbrev` set to `0`
git describe --abbrev=0
## output
v0.1.2

Sorting tags

Using the --sort option

From https://stackoverflow.com/questions/14273531/how-to-sort-git-tags-by-version-string-order-of-form-rc-x-y-z-w?#answer-22634649

With Git 2.0 (June 2014), you will be able to specify a sorting order!

Using --sort=<type>

Sort in a specific order. Supported type is: * "refname" (lexicographic order), * "version:refname" or "v:refname" (tag names are treated as versions).

Prepend "-" to reverse sort order.

So, if you have:

git tag foo1.3 &&
git tag foo1.6 &&
git tag foo1.10

Here is what you would get:

# lexical sort
git tag -l --sort=refname "foo*"
foo1.10
foo1.3
foo1.6

# version sort
git tag -l --sort=version:refname "foo*"
foo1.3
foo1.6
foo1.10

# reverse version sort
git tag -l --sort=-version:refname "foo*"
foo1.10
foo1.6
foo1.3

# reverse lexical sort
git tag -l --sort=-refname "foo*"
foo1.6
foo1.3
foo1.10

Examples using git tag to sort tags

Sort by -taggerdate

git tag --sort=-taggerdate
## output
v0.1.2
v0.1.1
v0
v0.1.0
v0.2
v0.2.0
v0.2.1

Sort by taggerdate

git tag --sort=taggerdate
## output
v0
v0.1.0
v0.2
v0.2.0
v0.2.1
v0.1.1
v0.1.2

Sort by -committerdate

git tag --sort=-committerdate
## output
v0.2.1
v0
v0.2
v0.2.0
v0.1.0
v0.1.1
v0.1.2

Sort by committerdate

git tag --sort=committerdate
## output
v0.1.1
v0.1.2
v0.1.0
v0
v0.2
v0.2.0
v0.2.1

Getting the latest tag

You can use any of the following commands to get the latest tag

Using git ls-remote

Using git ls-remote --tags --sort=committerdate:

git ls-remote --tags --sort=committerdate | grep -o 'v.*' | sort -r
## output
From https://github.com/rwaight/actions.git
v0.2.1
v0.2.0
v0.2
v0.1.2^{}
v0.1.2
v0.1.1^{}
v0.1.1
v0.1.0
v0

Using git ls-remote --tags --sort=committerdate | grep -o 'v.*':

git ls-remote --tags --sort=committerdate | grep -o 'v.*'
## output
v0.1.1
v0.1.2
v0.1.0
v0.1.1^{}
v0.1.2^{}
v0.2.0
v0
v0.2
v0.2.1

Using git ls-remote --tags --sort=taggerdate | grep -o 'v.*' | sort -r:

git ls-remote --tags --sort=taggerdate | grep -o 'v.*' | sort -r
## output
From https://github.com/rwaight/actions.git
v0.2.1
v0.2.0
v0.2
v0.1.2^{}
v0.1.2
v0.1.1^{}
v0.1.1
v0.1.0
v0

Using git tag

Using git tag --sort=-taggerdate | tail -1:

git tag --sort=-taggerdate | tail -1
## output
v0.2.1

Using git tag --sort=committerdate | grep -o 'v.*' | sort -r:

git tag --sort=committerdate | grep -o 'v.*' | sort -r
## output
v0.2.1
v0.2.0
v0.2
v0.1.2
v0.1.1
v0.1.0
v0

Using git tag --sort=committerdate | grep -o 'v.*' | sort -r | head -1:

git tag --sort=committerdate | grep -o 'v.*' | sort -r | head -1
## output
v0.2.1

Using git for-each-ref

Using git for-each-ref --sort=creatordate --format '%(refname) %(creatordate)' refs/tags:

git for-each-ref --sort=creatordate --format '%(refname) %(creatordate)' refs/tags
## output
refs/tags/v0.1.0 Mon Mar 11 12:10:23 2024 -0500
refs/tags/v0.1.1 Mon Mar 11 13:37:32 2024 -0500
refs/tags/v0.1.2 Mon Mar 11 13:50:12 2024 -0500
refs/tags/v0 Mon Mar 11 20:00:06 2024 -0500
refs/tags/v0.2 Mon Mar 11 20:00:06 2024 -0500
refs/tags/v0.2.0 Mon Mar 11 20:00:06 2024 -0500
refs/tags/v0.2.1 Thu Mar 14 11:39:55 2024 -0500

Examples using git tag
Examples using git tag (click to expand) ### Examples using git tag Using `git tag -l`:
git tag -l
## output
v0
v0.1.0
v0.1.1
v0.1.2
v0.2
v0.2.0
v0.2.1
Using `git tag -l | tail -1`:
git tag -l | tail -1
## output
v0.2.1
Using `git tag | sort -V`:
git tag | sort -V
## output
v0
v0.1.0
v0.1.1
v0.1.2
v0.2
v0.2.0
v0.2.1
Examples using git rev-list
Examples using git rev-list (click to expand) ### Examples using git rev-list Using `git rev-list --tags --max-count=1`:
git rev-list --tags --max-count=1
## output
d702f1832400f86753094a219e8383ae817ade34

Revert commits in git

Examples to revert changes in a repo, primarily using git revert. These examples come from the following sources:

  • https://stackoverflow.com/questions/3293531/how-to-permanently-remove-few-commits-from-remote-branch
  • https://christoph.ruegg.name/blog/git-howto-revert-a-commit-already-pushed-to-a-remote-reposit

Revert an already pushed commit

Important: Make sure you specify which branches on git push -f or you might inadvertently modify other branches![*]

Delete the last n commits

Example: Delete the last 4 commits:

git reset --hard HEAD~4

Then run the following command (on your local machine) to force the remote branch to rewrite its history:

git push --force

Delete to a specific commit ID

Retrieve the desired commit ID by running

git log

In the example, the desired commit is 8675309.

Then you can replace HEAD~N with the desired commit ID like this:

git reset --hard 8675309

If you want to keep changes on file system and just modify index (commit history), use --soft flag like git reset --soft HEAD~4. Then you have chance to check your latest changes and keep or drop all or parts of them. In the latter case running git status shows the files changed since <desired-commit-id>. If you use --hard option, git status will tell you that your local branch is exactly the same as the remote one. If you don't use --hard nor --soft, the default mode is used that is --mixed. In this mode, git help reset says:

Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit) and reports what has not been updated.

Revert the full commit

git revert 8675309

Delete the last commit

On a remote branch:

git push <<remote>> +8675309^:<<BRANCH_NAME_HERE>>

On a local branch:

git reset HEAD^ --hard
git push <<remote>> -f

Where +8675309 is your commit hash and git interprets x^ as the parent of x, and + as a forced non-fastforwared push.

Delete the commit from a list

git rebase -i 8675309^

This will open and editor showing a list of all commits. Delete the one you want to get rid off. Finish the rebase and push force to repo.

git rebase --continue
git push <remote_repo> <remote_branch> -f

Remove commits from remote without removing from local

clean way of removing your commits from the remote repository without losing your work

git reset --soft HEAD~1 # 1 represents only last 1 commit 
git stash # hold your work temporary storage temporarily.
git pull # bring your local in sync with remote
git reset --hard HEAD~1 # hard reset 1 commit behind (deletes local commit)
git push -f # force push to sync local with remote
git stash pop # get back your unpushed work from stash