Azure DevOps - Default permissions for force push on a branch

Monday, 8 July 2019

This error caught me by surprise today:

C:\dev\git\project [feature ↓1 ↑2 +1 ~0 -0 !]> git push --force-with-lease
Enumerating objects: 22, done.
Counting objects: 100% (22/22), done.
Delta compression using up to 8 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (12/12), 1.61 KiB | 824.00 KiB/s, done.
Total 12 (delta 6), reused 8 (delta 4)
remote: Analyzing objects... (12/12) (8 ms)
remote: Storing packfile... done (115 ms)
remote: Storing index... done (81 ms)
To https://mytenancy.visualstudio.com/repo/_git/project
 ! [remote rejected]       feature -> feature (TF401027: You need the Git 'ForcePush' permission to perform this action. Details: identity '93086622-abb9-4886-b994-502e7f2afc21\[email protected]', scope 'branch'.)
error: failed to push some refs to 'https://mytenancy.visualstudio.com/repo/_git/project'

I was trying to push a change to a Git branch in Azure DevOps Repos. I’d just done an interactive rebase to update history on the branch and was trying to force push my changes - something I’ve done countless times before.

Reviewing the permissions for this repository, the Force push (rewrite history, delete branches and tags) permission was Not Set (eg. neither explicitly allowed nor denied). Checking other projects and repositories, this was also the case, so what’s different?

Well one thing that’s different is that I didn’t create this branch - it was created by another developer. Force pushing to a shared branch is generally frowned upon (as if it isn’t coordinated it can cause all kinds of problems), and so it seems DevOps helps guard against this problem by defaulting to granting the Force Push permission just to the branch creator (and also to users who are the Project administrator - as set in the Project details page of Project Settings).

Azure DevOps Branch user permissions

Because I didn’t create the original branch and I was not a project administrator in this particular project, no push for me!

Check out the descriptions of the different permissions for Git repositories and the default Git repository and branch permissions.

In the garden - Winter 2019

Saturday, 6 July 2019

It’s been a while since my last garden update. Things are cooling down in Adelaide. Not to the extent of other places that get regular snow and ice, but still chilly enough.

Jonquils flowering

Our apple trees are starting to mature now and we’ve had good crops from both trees this year. The Pink Lady still has fruit hanging on the tree, and they’ve coloured up really nicely and the family are enjoying eating them.

Pink Lady apple tree bearing fruit

The mandarin is doing a great job. The fruit are so much tastier than the ones you buy in the shop. So far this year I have had no problems with possums/rodents eating the fruit, so I haven’t worried about netting the tree. The fruit might be a little smaller than previous years, possibly due to an incredibly dry January/February. Some late rains came just in time.

Mandarin tree with fruit

The lemonade just keeps powering on. Such a productive tree. I’ve had to give it a few haircuts as it’s really growing tall.

Lemonade tree with fruit

Narelle did a great job planting out some potted colour a few weeks ago, and we’re continuing to enjoy the results.

Dianthus flowering in a pot

And we got to enjoy some late flowers from this rose. Very pretty.

Orange rose in flower

Time to get back in the garden and do some weeding!

Congratulations 2019-2020 Microsoft MVP!

Tuesday, 2 July 2019

I received a really nice email last night:

MVP Letter

Great to be a Microsoft MVP for another year!

I first received the Microsoft MVP award in October 2015 (which happens to be about 14 months after I took over organising the Adelaide .NET User Group). Each year since then, I wonder if I’ll be renewed. So far, so good.

It’s a nice way for Microsoft to show their appreciation for the various things I do for the developer community - running the user group, speaking, and lots of open source contributions. It’s also been a great avenue for meeting new people, visiting new places and also providing feedback direct to Microsoft’s product teams.

It’s also something I’d love more people being recognised for (especially in Adelaide). If you are doing things in the development or IT community (or know someone who is), then let me know in the comments - I’d love to nominate them.

MVP Logo

TeamCity conditional build configurations - master vs other branches

Friday, 21 June 2019

TeamCity doesn’t currently support conditional build steps (Vote on this issue). So how can you have different steps for a master builds vs. other branches?

This is not an uncommon scenario - you might want to perform similar build steps for all builds, but for a master build there may be some extra steps (eg. publishing NuGet/NPM packages to a repository, triggering an external activity) that you don’t want to run for a branch build (eg. a build for a pull request).

It is possible to work around this limitation by creating multiple build configurations - one for master and another for non-master branches. But how can you do that efficiently? One approach is to enable Versioned Settings using Kotlin.

Kotlin is a language created by JetBrains that targets the JVM (and is now the recommended language for Android development). But it’s also one of the language choices when you enable Versioned Settings in TeamCity. (The other is XML, which will look familiar if you’ve ever played with TeamCity’s meta-runners).

I’m not a Kotlin expert, but I managed to figure out that you can use it to generate the different variations, such that when TeamCity parses the Kotlin, it ends up creating multiple build configurations.

One thing to be aware of is that TeamCity parses the Kotlin up-front to create the build configurations. It doesn’t evaluate it as the build runs (so it isn’t possible to have an expression that tests an environment variable or parameter)

The following example shows generating two build configurations - “Build_CI_Master” and “Build_CI_Branches”. The difference is that the Master build configuration enables package indexing. The build configurations use different branch filters to control whether they apply to master or non-master branches.

import jetbrains.buildServer.configs.kotlin.v2018_2.*
import jetbrains.buildServer.configs.kotlin.v2018_2.buildFeatures.nuGetPackagesIndexer
import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.PowerShellStep
import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.powerShell
import jetbrains.buildServer.configs.kotlin.v2018_2.projectFeatures.nuGetFeed
import jetbrains.buildServer.configs.kotlin.v2018_2.triggers.vcs
import jetbrains.buildServer.configs.kotlin.v2018_2.ui.*

version = "2018.2"

val variations = listOf("Master", "Branches")
project {

    for (type in variations) {
        buildType(KotlinExample(type))
    }

    features {
        nuGetFeed {
            id = "repository-nuget-project_feed"
            name = "project_feed"
            description = ""
        }
    }
}

class KotlinExample(val variant: String) : BuildType({
    id("Build_CI_${variant}".toId())

    name = "CI $variant"

    buildNumberPattern = "1.0.%build.counter%"

    vcs {
        add(DslContext.settingsRoot.id!!)
    }

    triggers {
        add {
            vcs {
                if (variant == "Master") {
                    branchFilter = "+:<default>"
                } else {
                    branchFilter = """
                            +:*
                            -:<default>
                        """.trimIndent()
                }
            }
        }
    }

    steps {

        step {
            /* additional steps */
        }
    }

    features {
        if (variant == "Master") {
            nuGetPackagesIndexer {
                feed = "project_feed/project_feed"
            }
        }
    }
})

Global DevOps Bootcamp 2019

Sunday, 16 June 2019

Yesterday (Saturday 15th) was the 3rd Global DevOps Bootcamp we’ve run in Adelaide.

Selfie Using the great facilities of the University of South Australia, a bunch of people gathered to learning more about DevOps, and in particular this year on the ‘Run’ part of DevOps, including introducing the role of a Site Reliability Engineer (SRE).

The day started off with a recorded introduction from international event organisers, then a keynote from Niall Murphy (Microsoft Ireland Director of Engineering for Azure Cloud Services and Site Reliability Engineering), followed by a local keynote delivered by me.

Keynote

After this people got into teams and worked through different challenges relating to a mythical online car-parts company that was experiencing all kinds of problems in production. Teams were encouraged to follow the pattern of ‘Detect, Respond and Recover’ - identifying the problem, putting in a quick fix to get the website back up and then implementing a long-term solution to prevent the problem from reoccurring.

Team challenges

The teams worked through the rest of the day, doing a great job on the challenges. The light-hearted video introductions to each challenge were a new thing this year and were a nice touch.

All credit to Rene, Marcel, Mathias and their team of helpers who put all the content together and coordinated a massive global event together with Microsoft.

Microsoft provided Subway for lunch and special thanks to my employer RLDatix who picked up the tab for fresh coffees for all attendees.