corto

module
v1.0.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 18, 2024 License: Apache-2.0

README ΒΆ

Corto

Incident tooling.

Building & testing

$ make
$ make unit-test
$ make CORTO_CONFIG_PATH=/path/to/config.yaml functional-test
Configuring Visual Studio Code (IDE)

Corto's tests use build tags to separate functional and unit tests. If you use Visual Studio Code you'll need to configure it for these tags. Warning: If you don't do this, you will see warnings of unused objects.

Add/edit the following in settings.json:

...
"go.buildTags": "functional,unit",
...

Releasing

To create a new release of Corto:

$ VERSION=x.y.z
$ DEBEMAIL=username@wikimedia.org dch -v "$VERSION"
$ git add debian/changelog
$ git commit -m "v$VERSION release"
$ git tag -a -m "v$VERSION release" "v$VERSION" HEAD
$ git push origin main
$ git push origin --tags
Creating a Debian package

Corto builds what is referred to as a Debian native package, which is a fancy way of saying that the packaging files (the contents of debian/) are in the main source tree; The software, and the packaging for the software are effectively treated as one in the same. This also means that Corto package versions do not contain a Debian packaging release number (-1, -2, etc). Any documentation that describes the use of branches to maintain the packaging can safely be ignored here.

Once a release has been prepared (see Releasing above), all that is required is to push the release sha to an appropriately named branch ({name}-wikimedia), and one of Gitlab's CI runners will create a package for you.

For example:

$ git checkout -b bookworm-wikimedia origin/bookworm-wikimedia
$ git merge "v$VERSION"
$ git push origin bookworm-wikimedia

Now you can visit pipelines and find the one that corresponds to your bookworm-wikimedia push. Assuming all stages completed successfully, click the final one (build_ci_deb), and then use Job artifacts on the right hand side of the page to download the Debian package artifacts.

NOTE: The branch that packages are built from ({name}-wikimedia) only needs to be a copy of the branch you release from (with its HEAD at the point you want to a release a package from). Do not make changes to this branch that would prevent a merge from being a fast-forward.

Using

Running an IRC bot
  1. Create a config.yaml as appropriate (see sample)
  2. Start the bot: ./cortobot -config config.yaml

See also: cortobot/README.markdown

Code conventions

Named return parameters

Go allows you to supply named return parameters in the function signature or definition. When present, these variables do not need to be declared within the function body, and a simple return statement (a naked return) automatically returns the named return parameters. Use of named return parameters has the potential to be confusing though, so as a general rule: If you use them it's probably best to standardize and use them everywhere. And if you do use them, it's probably best to adopt the convention of always returning the named parameters (naked or otherwise).

For Corto, we agreed to standardize on NOT using named return parameters.

Don't do this ☹️:

func divide(x, y int) (result int, err error) {
	if y == 0 {
		err = fmt.Errorf("Can't divide by zero!")
		return
	}
	result = x / y
	return
}

Do this instead 😊:

func divide(x, y int) (int, error) {
	var err error
	var result int
	if y == 0 {
		err = fmt.Errorf("Can't divide by zero!")
		return -1, err
	}
	result = x / y
	return result, nil
}
Error handling

A very common pattern in Go is to invoke a function that returns multiple results, one of which is of type error that is expected to be non-nil when there are failures. For example:

    res, err := divide(4, 0)
    if err != nil {
        log.Fatal("Oh no:", err)
    }

    fmt.Println("Result is:", res)

An alternative is to combine the assignment with the expression (an initialization statement):

    var err error
    var res int

    if res, err = divide(4, 0); err != nil {
        log.Fatal("Oh no:", err)
    }

    fmt.Println("Result is:", res)

This can read a bit better (it looks closer to the exception-handling seen in other languages). One caveat with this however, is that using type inference here would limit the scope of the variables to the enclosing block. To make use of a result outside the block, it needs to be pre-declared (in the example above, they both must be). This usually contributes to readability anyway by making scope (and types) more obvious (explicit v implicit).

For Corto: Initialization statements can improve readability, so consider them preferred, and use them whenever it makes sense.

Architecture

Disclaimer: What follows is more or less correct at the time of writing (correct in that it reflects the current implementation), but it is not the path forward. The idea that Corto could be a stateless application (that Phabricator could be the state) hasn't survived contact with reality.

See also: https://groups.google.com/a/wikimedia.org/g/sre-onfire/c/ZtBHtJCJDdk


                                                       β”‚                         
                                                       β”‚                         
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚                         
     β”‚               β”‚           β”‚               β”‚     β”‚                         
     β”‚ IRC           β”‚           β”‚ Web(?)        β”‚     β”‚                         
     β”‚               β”‚           β”‚               β”‚     β”‚      View(?)            
     β”‚               β”‚           β”‚               β”‚     β”‚                         
     β””β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚                         
             β”‚                           β”‚             β”‚                         
             β”‚                           β”‚             β”‚                         
             └───────────┐   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜             β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  
                         β”‚   β”‚                         β”‚                         
                         β”‚   β”‚                         β”‚                         
                    β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”΄β”€β”€β”€β”€β”                    β”‚                         
                    β”‚             β”‚                    β”‚                         
                    β”‚ Corto       β”‚                    β”‚                         
                    β”‚             β”‚                    β”‚                         
                    β”‚             β”‚                    β”‚      Controller(?)      
                    β”‚             β”‚                    β”‚                         
                    β”‚             β”‚                    β”‚                         
                    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                    β”‚                         
                         β”‚   β”‚                         β”‚                         
                         β”‚   β”‚                         β”‚                         
                         β”‚   β”‚                         β”‚                         
             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   └───────────┐             β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  
             β”‚                           β”‚             β”‚                         
             β”‚                           β”‚             β”‚                         
     β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”     β”‚                         
     β”‚               β”‚           β”‚               β”‚     β”‚                         
     β”‚ Phabricator   β”‚           β”‚ Google Docs   β”‚     β”‚       Model(?)          
     β”‚               β”‚           β”‚               β”‚     β”‚                         
     β”‚               β”‚           β”‚               β”‚     β”‚                         
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚                         
                                                       β”‚                         
                                                       β”‚                         
                                                       β”‚                         

ManiphestTaskManager implements the TaskManager interface, and completely encapsulates all interaction with Phabricator. So β€”for exampleβ€” while Gonduit is used to interface Phabricator directly, ManiphestTaskManager doesn't expose any its API or structures.

Similarly, GoogleDocument implements the Document interface, and encapsulates interaction with Google's APIs.

Instances of both of these are created in main.go and passed in during creation of the Corto instance.

The objective is to have a clear separation of concerns, and loose coupling. A change in one component becomes less likely to break others, and less coordination is needed amongst developers (think: fewer merge conflicts when merging long-lived branches). Mocking is possible (examples provided), and the code is more testable.

Corto here manages the high-level logic. It uses its ManiphestTaskManager to create a new templated issue, its GoogleDocument to create a new collab document, and then (using its ManiphestTaskManager) adds a reference to that in the metadata comment of the incident.

If you stand back and squint, it's almost an MVC architecture, where Phabricator (and Google) are state (model), Corto provides the business logic (controller), and IRC is presentation (view).

Directories ΒΆ

Path Synopsis
cmd
SPDX-License-Identifier: Apache-2.0 Copyright 2024 Eric Evans <eevans@wikimedia.org>, Wikimedia Foundation, Inc.
SPDX-License-Identifier: Apache-2.0 Copyright 2024 Eric Evans <eevans@wikimedia.org>, Wikimedia Foundation, Inc.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL