CONTRIBUTING.md (26554B)
1 # Contribution Guidelines <!-- omit in toc --> 2 3 Hey! Welcome to the CONTRIBUTING.md for GoToSocial :) Thanks for taking a look, that kicks ass. 4 5 These contribution guidelines were adapted from / inspired by those of Gitea (https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md). Thanks Gitea! 6 7 ## Table of Contents <!-- omit in toc --> 8 9 - [Introduction](#introduction) 10 - [Bug reports and feature requests](#bug-reports-and-feature-requests) 11 - [Pull requests](#pull-requests) 12 - [Code](#code) 13 - [Documentation](#documentation) 14 - [Development](#development) 15 - [Golang forking quirks](#golang-forking-quirks) 16 - [Building GoToSocial](#building-gotosocial) 17 - [Binary](#binary) 18 - [Docker](#docker) 19 - [With GoReleaser](#with-goreleaser) 20 - [Manually](#manually) 21 - [Stylesheet / Web dev](#stylesheet--web-dev) 22 - [Live Reloading](#live-reloading) 23 - [Project Structure](#project-structure) 24 - [Finding your way around the code](#finding-your-way-around-the-code) 25 - [Style / Linting / Formatting](#style--linting--formatting) 26 - [Testing](#testing) 27 - [Standalone Testrig with Semaphore](#standalone-testrig-with-semaphore) 28 - [Running automated tests](#running-automated-tests) 29 - [SQLite](#sqlite) 30 - [Postgres](#postgres) 31 - [CLI Tests](#cli-tests) 32 - [Federation](#federation) 33 - [Updating Swagger docs](#updating-swagger-docs) 34 - [CI/CD configuration](#cicd-configuration) 35 - [Release Checklist](#release-checklist) 36 - [What if something goes wrong?](#what-if-something-goes-wrong) 37 38 ## Introduction 39 40 This document contains important information that will help you to write a successful contribution to GoToSocial. Please read it carefully before opening a pull request! 41 42 ## Bug reports and feature requests 43 44 Currently, we use Github's issue system for tracking bug reports and feature requests. 45 46 You can view all open issues [here](https://github.com/superseriousbusiness/gotosocial/issues "The Github Issues page for GoToSocial"). 47 48 Before opening a new issue, whether bug or feature request, **please search carefully through both open and closed issues to make sure it hasn't been addressed already**. You can use Github's keyword issue search for this. If your issue is a duplicate of an existing issue, it will be closed. 49 50 Before you open a feature request issue, please consider the following: 51 52 - Does this feature fit within the scope of GoToSocial? Since we are a small team, we are wary of [feature creep](https://en.wikipedia.org/wiki/Feature_creep "Wikipedia article on Feature Creep"), which can cause maintenance issues. 53 - Will this feature be generally useful for many users of the software, or is it handy for only a very specific use case? 54 - Will this feature have a negative impact on the performance of the software? If so, is the tradeoff worth it? 55 - Does this feature require loosening API security restrictions in some way? If so, it will need a good justification. 56 - Does this feature belong in GoToSocial's server backend, or is it something that a client could implement instead? 57 58 We tend to prioritize feature requests related to accessibility, fedi interoperability, and client compatibility. 59 60 ## Pull requests 61 62 We welcome pull requests from new and existing contributors, with the following provisos: 63 64 - You have read and agree to our Code of Conduct. 65 - The pull request addresses an existing issue or bug (please link to the relevant issue in your pull request), or is related to documentation. 66 - If your pull request introduces significant new code paths, you are willing to do some maintenance work on those code paths, and address bugs. We do not appreciate drive-by pull requests that introduce a significant maintenance burden! 67 - The pull request is of decent quality. We are a small team and unfortunately we don't have a lot of time to help shepherd pull requests, or help with basic coding questions. If you're unsure, don't bite off more than you can chew: start with a small feature or bugfix for your first PR, and work your way up. 68 69 If you have small questions or comments before/during the pull request process, you can [join our Matrix space](https://matrix.to/#/#gotosocial-space:superseriousbusiness.org "GoToSocial Matrix space") at `#gotosocial-space:superseriousbusiness.org`. 70 71 Please read the appropriate section below for the kind of pull request you plan to open. 72 73 ### Code 74 75 To keep things manageable for maintainers, the process for opening pull requests against the GoToSocial repository works roughly as follows: 76 77 1. Open an issue for the feature, bug, or issue your pull request will address, or comment on an existing issue to let everyone know you want to work on it. 78 2. Use the open issue to discuss your design with us, gather feedback, and resolve any concerns about the implementation. 79 3. Write your code! Make sure all existing tests pass. Add tests where appropriate. Run linters and formatters. Update documentation. 80 4. Open your pull request. You can do this as a draft, if you want to gather more feedback on code-in-progress. 81 5. Let us know that your pull request is ready to be reviewed. 82 6. Wait for review. 83 7. Address review comments, make changes to the code where appropriate. It's OK to push back on review comments if you have a sensible reason--we're all learning, after all--but please do so with patience and grace. 84 8. Get your code merged, rejoice! 85 86 To make your code easier to review, try to split your pull request into sensibly-sized commits, but don't worry too much about making it totally perfect: we always squash merge anyways. 87 88 If your pull request ends up being massive, consider splitting it into smaller discrete pull requests to make it easier to review and reason about. 89 90 Make sure your pull request only contains code that's relevant to the feature you're trying to implement, or the bug you're trying to address. Don't include refactors of unrelated parts of the code in your pull request: make a separate PR for that! 91 92 If you open a code pull request without following the above process, we may close it and ask you to follow the process instead. 93 94 ### Documentation 95 96 The process for documentation pull requests is a bit looser than the process for code. 97 98 If you see something in the documentation that's missing, wrong, or unclear, feel free to open a pull request addressing it; you don't necessarily need to open an issue first, but please explain why you're opening the PR in the PR comment. 99 100 We support a [Conda](https://docs.conda.io/en/latest/)-based workflow for hacking, building & publishing the documentation. Here's how you can get started locally: 101 102 * Install [`miniconda`](https://docs.conda.io/en/latest/miniconda.html) 103 * Create your conda environment: `conda env create -f ./docs/environment.yml` 104 * Activate the environment: `conda activate gotosocial-docs` 105 * Serve locally: `mkdocs serve` 106 107 Then you can visit [localhost:8000](http://127.0.0.1:8000/) in your browser to view. 108 109 If you don't use Conda, you can read the `docs/environment.yml` to see which dependencies are required and `pip install` them manually. 110 111 ## Development 112 113 ### Golang forking quirks 114 115 One of the quirks of Golang is that it relies on the source management path being the same as the one used within `go.mod` and in package imports within individual Go files. This makes working with forks a bit awkward. 116 117 Let's say you fork GoToSocial to `github.com/yourgithubname/gotosocial`, and then clone that repository to `~/go/src/github.com/yourgithubname/gotosocial`. You will probably run into errors trying to run tests or build, so you might change your `go.mod` file so that the module is called `github.com/yourgithubname/gotosocial` instead of `github.com/superseriousbusiness/gotosocial`. But then this breaks all the imports within the project. Nightmare! So now you have to go through the source files and painstakingly replace `github.com/superseriousbusiness/gotosocial` with `github.com/yourgithubname/gotosocial`. This works OK, but when you decide to make a pull request against the original repo, all the changed paths are included! Argh! 118 119 The correct solution to this is to fork, then clone the upstream repository, then set `origin` of the upstream repository to that of your fork. 120 121 See [this blog post](https://blog.sgmansfield.com/2016/06/working-with-forks-in-go/) for more details. 122 123 In case this post disappears, here are the steps (slightly modified): 124 125 > 126 > Fork the repository on GitHub or set up whatever other remote git repo you will be using. In this case, I would go to GitHub and fork the repository. 127 > 128 > Now clone the upstream repo (not the fork): 129 > 130 > `mkdir -p ~/go/src/github.com/superseriousbusiness && git clone git@github.com:superseriousbusiness/gotosocial ~/go/src/github.com/superseriousbusiness/gotosocial` 131 > 132 > Navigate to the top level of the upstream repository on your computer: 133 > 134 > `cd ~/go/src/github.com/superseriousbusiness/gotosocial` 135 > 136 > Rename the current origin remote to upstream: 137 > 138 > `git remote rename origin upstream` 139 > 140 > Add your fork as origin: 141 > 142 > `git remote add origin git@github.com/yourgithubname/gotosocial` 143 > 144 145 ### Building GoToSocial 146 147 #### Binary 148 149 To get started, you first need to have Go installed. GtS is currently using Go 1.20, so you should take that too. See [here](https://golang.org/doc/install) for installation instructions. 150 151 Once you've got go installed, clone this repository into your Go path. Normally, this should be `~/go/src/github.com/superseriousbusiness/gotosocial`. 152 153 Once you've installed the prerequisites, you can try building the project: `./scripts/build.sh`. This will build the `gotosocial` binary. 154 155 If there are no errors, great, you're good to go! 156 157 For automatic re-compiling during development, you can use [nodemon](https://www.npmjs.com/package/nodemon): 158 159 ```bash 160 nodemon -e go --signal SIGTERM --exec "go run ./cmd/gotosocial --host localhost testrig start || exit 1" 161 ``` 162 163 #### Docker 164 165 For both of the below methods, you need to have [Docker buildx](https://docs.docker.com/buildx/working-with-buildx/) installed. 166 167 ##### With GoReleaser 168 169 GoToSocial uses the release tooling [GoReleaser](https://goreleaser.com/intro/) to make multiple-architecture + Docker builds simple. 170 171 GoReleaser is also used by GoToSocial for building and pushing Docker containers. 172 173 Normally, these processes are handled by Drone (see CI/CD above). However, you can also invoke GoReleaser manually for things like building snapshots. 174 175 To do this, first [install GoReleaser](https://goreleaser.com/install/). 176 177 Then install GoSwagger as described in [the Swagger section](#updating-swagger-docs). 178 179 Then install Node and Yarn as described in [Stylesheet / Web dev](#stylesheet--web-dev). 180 181 Finally, to create a snapshot build, do: 182 183 ```bash 184 goreleaser --rm-dist --snapshot 185 ``` 186 187 If all goes according to plan, you should now have a number of multiple-architecture binaries and tars inside the `./dist` folder, and snapshot Docker images should be built (check your terminal output for version). 188 189 ##### Manually 190 191 If you prefer a simple approach to building a Docker container, with fewer dependencies (go-swagger, Node, Yarn), you can also just build in the following way: 192 193 ```bash 194 ./scripts/build.sh && docker buildx build -t superseriousbusiness/gotosocial:latest . 195 ``` 196 197 The above command first builds the `gotosocial` binary, then invokes Docker buildx to build the container image. 198 199 If you want to build a docker image for a different CPU architecture without setting up buildx (for example for ARMv7 aka 32-bit ARM), first modify the Dockerfile by adding the following lines to the top (but don't commit this!): 200 201 ```dockerfile 202 # When using buildx, these variables will be set by the tool: 203 # https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope 204 # However, declaring them as global build arguments like this allows them to be set manually with `--build-arg` instead. 205 ARG BUILDPLATFORM 206 ARG TARGETPLATFORM 207 ``` 208 209 Then, you can use the following command: 210 211 ```bash 212 GOOS=linux GOARCH=arm ./scripts/build.sh && docker build --build-arg BUILDPLATFORM=linux/amd64 --build-arg TARGETPLATFORM=linux/arm/v7 -t superseriousbusiness/gotosocial:latest . 213 ``` 214 215 See also: [exhaustive list of GOOS and GOARCH values](https://gist.github.com/lizkes/975ab2d1b5f9d5fdee5d3fa665bcfde6) 216 217 And: [exhaustive list of possible values for docker's `--platform`](https://github.com/tonistiigi/binfmt/#build-test-image) 218 219 ### Stylesheet / Web dev 220 221 GoToSocial uses Gin templates in the `web/template` folder. Static assets are stored in `web/assets`. Source files for stylesheets and JS bundles (for frontend enhancement, and the settings interface) are stored in `web/source`, and bundled from there to the `web/assets/dist` folder (gitignored). 222 223 To bundle changes, you need [Node.js](https://nodejs.org/en/download/) and [Yarn](https://classic.yarnpkg.com/en/docs/install). 224 225 Using [NVM](https://github.com/nvm-sh/nvm) is one convenient way to install them which also supports managing different Node versions. 226 227 To install Yarn dependencies: 228 229 ```bash 230 yarn install --cwd web/source 231 ``` 232 233 To recompile bundles: 234 235 ```bash 236 node web/source 237 ``` 238 239 #### Live Reloading 240 241 For a more convenient development environment, you can run a livereloading version of the bundler alongside the [testrig](#testing). 242 243 Open two terminals, first start the testrig on port 8081: 244 ``` bash 245 GTS_PORT=8081 go run ./cmd/gotosocial testrig start 246 ``` 247 Then start the bundler, which will run on port 8080, and proxy requests to the testrig instance where needed. 248 ``` bash 249 NODE_ENV=development node web/source 250 ``` 251 252 The livereloading bundler *will not* change the bundled assets in `dist/`, so once you are finished with changes and want to deploy it somewhere, you have to run `node web/source` to generate production-ready bundles. 253 254 ### Project Structure 255 256 For project structure, GoToSocial follows a standard and widely accepted project layout [defined here](https://github.com/golang-standards/project-layout). As the author writes: 257 258 > This is a basic layout for Go application projects. It's not an official standard defined by the core Go dev team; however, it is a set of common historical and emerging project layout patterns in the Go ecosystem. 259 260 Where possible, we prefer more files and packages of shorter length that very clearly pertain to definable chunks of application logic, rather than fewer but longer files: if one `.go` file is pushing 1,000 lines of code, it's probably too long. 261 262 #### Finding your way around the code 263 264 Most of the crucial business logic of the application is found inside the various packages and subpackages of the `internal` directory. Here's a brief summary of each of these: 265 266 `internal/ap` - ActivityPub utility functions and interfaces. 267 268 `internal/api` - Models, routers, and utilities for the client and federated (ActivityPub) APIs. This is where routes are attached to the router, and where you want to be if you're adding a route. 269 270 `internal/concurrency` - Worker models used by the processor and other queues. 271 272 `internal/config` - Code for configuration flags, CLI flag parsing, and getting/setting config. 273 274 `internal/db` - DB interfaces for interacting with sqlite/postgres databases. Database migration code is in `internal/db/bundb/migrations`. 275 276 `internal/email` - Email functionality, email sending via SMTP. 277 278 `internal/federation` - ActivityPub federation code; implements `go-fed` interfaces. 279 280 `internal/federation/federatingdb` - Implementation of `go-fed`'s Database interface. 281 282 `internal/federation/dereferencing` - Code for making http calls to fetch resources from remote instances. 283 284 `internal/gotosocial` - GoToSocial server startup/shutdown logic. 285 286 `internal/gtserror` - Error models. 287 288 `internal/gtsmodel` - Database and internal models. This is where `bundb` annotations live. 289 290 `internal/httpclient` - The HTTP client used by GoToSocial for making requests to remote resources. 291 292 `internal/id` - Code for generating IDs (ULIDs) for database models. 293 294 `internal/log` - Our logging implementation. 295 296 `internal/media` - Code related to managing + processing media attachments; images, video, emojis, etc. 297 298 `internal/messages` - Models for wrapping worker messages. 299 300 `internal/middleware` - Gin Gonic router middlewares: http signature checking, cache control, token checks, etc. 301 302 `internal/netutil` - HTTP / net request validation code. 303 304 `internal/oauth` - Wrapper code/interfaces for OAuth server implementation. 305 306 `internal/oidc` - Wrapper code/interfaces for OIDC claims and callbacks. 307 308 `internal/processing` - Logic for processing messages produced by the federation or client APIs. Much of the core business logic of GoToSocial is contained here. 309 310 `internal/regexes` - Regular expressions used for text parsing and matching of URLs, hashtags, mentions, etc. 311 312 `internal/router` - Wrapper for Gin HTTP router. Core HTTP logic is contained here. The router exposes functions for attaching routes, which are used by the code in `internal/api` handlers. 313 314 `internal/storage` - Wrapper for `codeberg.org/gruf/go-store` implementations. Local file storage and s3 logic goes here. 315 316 `internal/stream` - Websockets streaming logic. 317 318 `internal/text` - Text parsing and transformation. Status parsing logic is contained here -- both plain and markdown. 319 320 `internal/timeline` - Status timeline management code. 321 322 `internal/trans` - Code for exporting models to json backup files from the database, and importing backup json files into the database. 323 324 `internal/transport` - HTTP transport code and utilities. 325 326 `internal/typeutils` - Code for converting from internal database models to json, and back, or from ActivityPub format to internal database model format and vice versa. Basically, serdes. 327 328 `internal/uris` - Utilities for generating URIs used throughout GoToSocial. 329 330 `internal/util` - Odds and ends; small utility functions used by more than one package. 331 332 `internal/validate` - Model validation code -- currently not really used. 333 334 `internal/visibility` - Status visibility checking and filtering. 335 336 `internal/web` - Web UI handlers, specifically for serving web pages, the landing page, settings panels. 337 338 ### Style / Linting / Formatting 339 340 It is a good idea to read the short official [Effective Go](https://golang.org/doc/effective_go) page before submitting code: this document is the foundation of many a style guide, for good reason, and GoToSocial more or less follows its advice. 341 342 Another useful style guide that we try to follow: [this one](https://github.com/bahlo/go-styleguide). 343 344 In addition, here are some specific highlights from Uber's Go style guide which agree with what we try to do in GtS: 345 346 - [Group Similar Declarations](https://github.com/uber-go/guide/blob/master/style.md#group-similar-declarations). 347 - [Reduce Nesting](https://github.com/uber-go/guide/blob/master/style.md#reduce-nesting). 348 - [Unnecessary Else](https://github.com/uber-go/guide/blob/master/style.md#unnecessary-else). 349 - [Local Variable Declarations](https://github.com/uber-go/guide/blob/master/style.md#local-variable-declarations). 350 - [Reduce Scope of Variables](https://github.com/uber-go/guide/blob/master/style.md#reduce-scope-of-variables). 351 - [Initializing Structs](https://github.com/uber-go/guide/blob/master/style.md#initializing-structs). 352 353 Before you submit any code, make sure to run `go fmt ./...` to update whitespace and other opinionated formatting. 354 355 We use [golangci-lint](https://golangci-lint.run/) for linting, which allows us to catch style inconsistencies and potential bugs or security issues, using static code analysis. 356 357 If you make a PR that doesn't pass the linter, it will be rejected. As such, it's good practice to run the linter locally before pushing or opening a PR. 358 359 To do this, first install the linter following the instructions [here](https://golangci-lint.run/usage/install/#local-installation). 360 361 Then, you can run the linter with: 362 363 ```bash 364 golangci-lint run 365 ``` 366 367 If there's no output, great! It passed :) 368 369 ### Testing 370 371 GoToSocial provides a [testrig](https://github.com/superseriousbusiness/gotosocial/tree/main/testrig) with a number of mock packages you can use in integration tests. 372 373 One thing that *isn't* mocked is the Database interface because it's just easier to use an in-memory SQLite database than to mock everything out. 374 375 #### Standalone Testrig with Semaphore 376 377 You can launch a testrig as a standalone server running at localhost, which you can connect to using something like [Semaphore](https://github.com/NickColley/semaphore/). 378 379 To do this, first build the gotosocial binary with `DEBUG=1 ./scripts/build.sh`. 380 381 Then, launch the testrig with the `DEBUG` environment variable set by invoking the binary as follows: 382 383 ```bash 384 DEBUG=1 ./gotosocial testrig start 385 ``` 386 387 To run Semaphore locally in dev mode, first clone the [Semaphore](https://github.com/NickColley/semaphore/) repository, and then run the following commands in the cloned directory: 388 389 ```bash 390 yarn # install dependencies 391 yarn run dev 392 ``` 393 394 The Semaphore instance will start running on `localhost:4002`. 395 396 To connect to the testrig, navigate to `http://localhost:4002` and enter your instance name as `localhost:8080`. 397 398 At the login screen, enter the email address `zork@example.org` and password `password`. You will get a confirmation prompt. Accept, and you are logged in as Zork. 399 400 Note the following constraints: 401 402 - Since the testrig uses an in-memory database, the database will be destroyed when the testrig is stopped. 403 - If you stop the testrig and start it again, any tokens or applications you created during your tests will also be removed. As such, you need to log out and in again every time you stop/start the rig. 404 - The testrig does not make any actual external HTTP calls, so federation will not work from a testrig. 405 406 #### Running automated tests 407 408 Tests can be run against both SQLite and Postgres. 409 410 ##### SQLite 411 412 If you would like to run tests as quickly as possible, using an SQLite in-memory database, use: 413 414 ```bash 415 go test ./... 416 ``` 417 418 ##### Postgres 419 420 If you want to run tests against a Postgres database on localhost, run: 421 422 ```bash 423 GTS_DB_TYPE="postgres" GTS_DB_ADDRESS="localhost" go test -p 1 ./... 424 ``` 425 426 In the above command, it is assumed you are using the default Postgres password of `postgres`. 427 428 We set `-p 1` when running against Postgres because it requires tests to run in serial, not in parallel. 429 430 #### CLI Tests 431 432 In [./test/envparsing.sh](./test/envparsing.sh) there's a test for making sure that CLI flags, config, and environment variables get parsed as expected. 433 434 Although this test *is* part of the CI/CD testing process, you probably won't need to worry too much about running it yourself. That is, unless you're messing about with code inside the `main` package in `cmd/gotosocial`, or inside the `config` package in `internal/config`. 435 436 #### Federation 437 438 By using the support for loading TLS files from disk it is possible to have two local instances with TLS to allow for (manually) testing federation. 439 440 You'll need to set the following configuration options: 441 * `GTS_TLS_CERTIFICATE_CHAIN`: poiting to a PEM-encoded certificate chain including the public certificate 442 * `GTS_TLS_CERTIFICATE_KEY`: pointing to a PEM-encoded private key 443 444 Additionally, for the Go HTTP client to recognise certificates issued by a custom CA as valid, you'll need to set one of: 445 * `SSL_CERT_FILE`: pointing to the public key of your custom CA 446 * `SSL_CERT_DIR`: a `:`-separated list of directories to load CA certificates from 447 448 You'll additionally need functioning DNS for your two instance names which you can achieve through entries in `/etc/hosts` or by running a local DNS server like [dnsmasq](https://thekelleys.org.uk/dnsmasq/doc.html). 449 450 ### Updating Swagger docs 451 452 GoToSocial uses [go-swagger](https://goswagger.io) to generate Swagger API documentation from code annotations. 453 454 You can install go-swagger following the instructions [here](https://goswagger.io/install.html). 455 456 If you change Swagger annotations on any of the API paths, you can generate a new Swagger file at `./docs/api/swagger.yaml` by running: 457 458 ```bash 459 swagger generate spec --scan-models --exclude-deps -o docs/api/swagger.yaml 460 ``` 461 462 ### CI/CD configuration 463 464 GoToSocial uses [Drone](https://www.drone.io/) for CI/CD tasks like running tests, linting, and building Docker containers. 465 466 These runs are integrated with GitHub, and will be run on opening a pull request or merging into main. 467 468 The Drone instance for GoToSocial is [here](https://drone.superseriousbusiness.org/superseriousbusiness/gotosocial). 469 470 The `drone.yml` file is [here](./.drone.yml) — this defines how and when Drone should run. Documentation for Drone is [here](https://docs.drone.io/). 471 472 It is worth noting that the `drone.yml` file must be signed by the Drone admin account to be considered valid. This must be done every time the file is changed. This is to prevent tampering and hijacking of the Drone instance. See [here](https://docs.drone.io/signature/). 473 474 To sign the file, first install and setup the [drone cli tool](https://docs.drone.io/cli/install/). Then, run: 475 476 ```bash 477 drone -t PUT_YOUR_DRONE_ADMIN_TOKEN_HERE -s https://drone.superseriousbusiness.org sign superseriousbusiness/gotosocial --save 478 ``` 479 480 ### Release Checklist 481 482 First things first: If this is a security hot-fix, we'll probably rush through this list, and make a prettier release a few days later. 483 484 Now, with that out of the way, here's our Checklist. 485 486 GoToSocial follows [Semantic Versioning](https://semver.org/). 487 So our first concern on the Checklist is: 488 489 - What version are we releasing? 490 491 Next we need to check: 492 493 - Do the assets have to be rebuilt and committed to the repository. 494 - Do the swagger docs have to be rebuilt? 495 496 On the project management side: 497 498 - Are there any issues that have to be moved to a different milestone? 499 - Are there any things on the [Roadmap](./ROADMAP.md) that can be ticked off? 500 501 Once we're happy with our Checklist, we can create the tag, and push it. 502 And the rest [is automation](./.drone.yml). 503 504 We can now head to GitHub, and add some personality to the release notes. 505 Finally, we make announcements on the all our channels that the release is out! 506 507 #### What if something goes wrong? 508 509 Sometimes things go awry. 510 We release a buggy release, we forgot something something important. 511 512 If the release is so bad that it's unusable or dangerous! to a great part of our user-base, we can pull. 513 That is: Delete the tag. 514 515 Either way, once we've resolved the issue, we just start from the top of this list again. Version numbers are cheap. It's cheap to burn them.