github.com/ddev/ddev@v1.23.2-0.20240519125000-d824ffe36ff3/docs/content/developers/building-contributing.md (about) 1 --- 2 search: 3 boost: .5 4 --- 5 # Building, Testing, and Contributing 6 7 ## Testing Latest Commits on HEAD 8 9 There are several ways to use DDEV’s latest-committed HEAD version: 10 11 * **Download** the latest master branch artifacts from [nightly.link](https://nightly.link/ddev/ddev/workflows/master-build/master). Each of these is built by the CI system, signed, and notarized. Get the one you need and place it in your `$PATH`. 12 * **Homebrew install HEAD**: On macOS and Linux, run `brew unlink ddev && brew install ddev/ddev/ddev --HEAD --fetch-HEAD` to get the latest DDEV commit, even if it’s unreleased. Since you’re building this on your own computer, it’s not signed or notarized, and you’ll get a notification that instrumentation doesn’t work, which is fine. If you’re using Linux/WSL2, you’ll likely need to install build-essential by running the following command: `sudo apt install -y build-essential`. 13 * **Build manually**: If you have normal build tools like `make` and `go` installed, you can check out the code and run `make`. 14 * **Gitpod** You can use the latest build by visiting DDEV on [Gitpod](https://gitpod.io/#https://github.com/ddev/ddev). 15 16 ## Testing a PR 17 18 Each [PR build](https://github.com/ddev/ddev/actions/workflows/pr-build.yml) creates GitHub artifacts you can use for testing, so you can download the one you need from the PR page, install it locally, and test using that build. 19 20 Download and unzip the appropriate binary and place it in your `$PATH`. 21 22 ### Homebrew with macOS or Linux 23 24 If you’re using Homebrew, start by unlinking your current binary: 25 26 ``` 27 brew unlink ddev 28 ``` 29 30 Next, unzip the binary you downloaded, make it executable, and move it to your bin folder: 31 32 ``` 33 unzip ddev.zip 34 chmod +x ddev && sudo mv ddev /usr/local/bin/ddev 35 ``` 36 37 Verify the replacement worked by running `ddev -v`. The output should be something like `ddev version v1.22.5-alpha1-70-g0852fc2df`, instead of the regular `ddev version v1.22.5`. 38 39 !!!tip "macOS and Unsigned Binaries" 40 macOS doesn’t like these downloaded binaries, so you’ll need to bypass the automatic quarantine to use them: 41 42 ``` 43 xattr -r -d com.apple.quarantine /usr/local/bin/ddev 44 ``` 45 46 (The binaries on the master branch and the final release binaries _are_ signed.) 47 48 You do not typically have to install anything else other than the downloaded binary; when you run it it will access any Docker images that it needs. 49 50 After you’re done, you can delete your downloaded binary and re-link the original Homebrew one: 51 52 ``` 53 sudo rm /usr/local/bin/ddev 54 brew link --force ddev 55 ``` 56 57 ### Installing a Downloaded Binary in the `$PATH` 58 59 Normally, you can put any executable in your path, and it takes precedence, so you don't need to remove or disable an already installed DDEV instance, which we will use here. This example uses `~/bin`. `echo $PATH` and `which ddev` are valuable commands for debugging. Since not every distro has `~/bin` in `$PATH`, you can create the folder and add it to your path in `~/.bashrc` with these commands: 60 61 ``` 62 mkdir -p ~/bin 63 export PATH="~/bin:$PATH" 64 ``` 65 66 Next, unzip the ZIP file you downloaded, make it executable, and move it to a folder in your path. Check with `echo $PATH`: 67 68 ``` 69 unzip ddev.zip 70 chmod +x ddev && mv ddev ~/bin 71 ``` 72 73 Now, close and reopen your terminal, and verify the replacement worked by running `ddev version`. The output should be something like `DDEV version v1.22.3-39-gfbb878843`, instead of the regular `DDEV version v1.22.3`. 74 75 You need to run `ddev poweroff` and `ddev start` to download the Docker images that it needs. 76 77 After you’re done testing, you can delete your downloaded executable, restart your terminal, and again use the standard DDEV: 78 79 ``` 80 rm ~/bin/ddev 81 ``` 82 83 ## Open in Gitpod 84 85 [Gitpod](https://www.gitpod.io) provides a quick, preconfigured DDEV experience in the browser for testing a PR easily without the need to set up an environment. For any PR you can use the URL `https://gitpod.io/#https://github.com/ddev/ddev/pull/<YOUR-PR>` to open that PR and build it in Gitpod. 86 87 To open and work on DDEV master branch you can use the button below. 88 [](https://gitpod.io/#https://github.com/ddev/ddev) 89 90 If you want to run a web project, you can check it out into `/workspace/<yourproject>` and use it as usual. The things you’re familiar with work normally, except that `ddev-router` does not run. 91 92 A Gitpod dummy project for is provided by default in `/workspace/d10simple`. If you’re testing your own project, you will need to delete it to free up reserved host ports by running `ddev delete -Oy d10simple`. Then you can run [`ddev start`](../users/usage/commands.md#start) to work with your own. 93 94 ## Making Changes to DDEV Images 95 96 If you need to make a change to one of the DDEV images, it will need to be built with a specific tag that’s updated in `pkg/versionconstants/versionconstants.go`. 97 98 For example, make a change to `containers/ddev-webserver/Dockerfile`, then build it: 99 100 ```bash 101 cd containers/ddev-webserver 102 make VERSION=20210424_fix_dockerfile 103 ``` 104 105 Then edit `pkg/versionconstants/versionconstants.go` to set `var WebTag = "20210424_fix_dockerfile"` and 106 107 ```bash 108 cd /workspace/ddev 109 make 110 ``` 111 112 `ddev version` should show you that you are using the correct webtag, and [`ddev start`](../users/usage/commands.md#start) will show it. 113 114 It’s easiest to do this using Gitpod (see above) because Gitpod already has `docker buildx` all set up for you and the built DDEV binary is in the `$PATH`. 115 116 ## Pull Requests and PR Preparation 117 118 When preparing your pull request, please use a branch name like `YYYYMMDD_<your_username>_short_description` (like `20230901_rfay_short_description`) so it’s easy to identify you as the author. 119 120 ## Docker Image Changes 121 122 If you make changes to a Docker image (like `ddev-webserver`), it won’t have any effect unless you: 123 124 * Push an image with a specific tag by navigating to the image directory (like `containers/ddev-webserver`), and running `make push DOCKER_REPO=youruser/yourimage VERSION=<branchname>`. 125 * Multi-arch images require you to have a Buildx builder, so `docker buildx create --name ddev-builder-multi --use`. 126 * You can’t push until you `docker login`. 127 * Push a container to hub.docker.com. Push with the tag that matches your branch. Push to `<yourorg>/ddev-webserver` repository with `make push DOCKER_ORG=<yourorg> VERSION=<branchname>` **in the container directory**. You might have to use other techniques to push to another repository. 128 * Update `pkg/versionconstants/versionconstants.go` with the `WebImg` and `WebTag` that relate to the Docker image you pushed. 129 130 ### Local Builds and Pushes 131 132 To use `buildx` successfully you have to have the [`buildx` Docker plugin](https://docs.docker.com/buildx/working-with-buildx/), which is in many environments by default. 133 134 To build multi-platform images you must `docker buildx create --use` as a one-time initialization. 135 136 * If you want to work locally with a quick build for your architecture, you can: 137 * `make VERSION=<version>` 138 * for `ddev-dbserver`: `make mariadb_10.3 VERSION=<version>` etc. 139 140 * To push manually: 141 142 ```markdown 143 cd containers/ddev-webserver 144 make push VERSION=<tag> 145 ``` 146 147 If you’re pushing to a repository other than the one wired into the Makefile (like `ddev/ddev-webserver`): 148 149 ``` 150 cd containers/ddev-webserver 151 make push VERSION=<tag> DOCKER_REPO=your/dockerrepo 152 ``` 153 154 ### Pushes Using GitHub Actions 155 156 To manually push using GitHub Actions, 157 158 #### For Most Images 159 160 * Visit [Actions → Push tagged image](https://github.com/ddev/ddev/actions/workflows/push-tagged-image.yml) 161 * Click “Run workflow” in the blue band near the top. 162 * Choose the branch, usually `master` and then the image to be pushed, `ddev-webserver`, `ddev-dbserver`, etc. Also you can use `all` to build and push all of them. Include a tag for the pushed image and GitHub will do all the work. 163 164 #### For `ddev-dbserver` 165 166 * Visit [Actions → Push tagged db image](https://github.com/ddev/ddev/actions/workflows/push-tagged-dbimage.yml) 167 * Click “Run workflow” in the blue band near the top. 168 * Choose the branch, usually `master`. Include a tag for the pushed image and GitHub will do all the work. 169 170 ## Instrumentation 171 172 The instrumentation implementation is generated using the [Ampli Codegen](https://www.docs.developers.amplitude.com/data/sdks/go/ampli/). 173 174 To synchronize the implementation with the latest changes at Amplitude, the CLI 175 tool has to be installed locally: 176 177 ```bash 178 npm install -g @amplitude/ampli 179 ``` 180 181 Make changes to the event definition using the GUI at <https://data.amplitude.com/ddev/DDEV>: 182 183 * create a new branch 184 * create or change events and properties 185 * save changes to the new branch 186 * update the implementation with `ampli checkout <branch name>` 187 * make changes to the code 188 189 Once finished, save the changes to publish a new version of the definitions. 190 191 Afterwards the changes can be imported running the following command in the 192 project root: 193 194 ```bash 195 ampli pull 196 ``` 197 198 Once the changes are ready to be merged, merge the changes made in the new 199 branch to the main branch in the Amplitude backend and switch back to the 200 main branch: 201 202 ```bash 203 ampli checkout main 204 ``` 205 206 Make sure the API keys are not included to the sources; they are linked during 207 compilation using a GitHub secret. 208 209 ### Environments 210 211 There are two environments defined, `DDEV - Production` and `DDEV - Development`. 212 Master builds will deliver the data to production, PR builds to development. 213 214 When working on Amplitude, please always make sure the correct environment is 215 selected or you won't see any data. Selection is possible on most pages. 216 217 ### User and event data 218 219 The first step is always to identify the device, this includes data like OS, 220 architecture, DDEV version, Docker, etc., details are visible in the 221 *User Properties*. The devices are called *Users* in the Amplitude backend. So 222 every user represents an unique device on which DDEV is installed. 223 224 The second step is to collect data about the command which was called by the 225 user and is delivered by a dedicated `Command` event. 226 227 The `Project` event finally collects data about the loaded project(s) which 228 includes important configuration details like PHP version, database, etc. 229 230 ### Debugging 231 232 Information about data debugging can be found at <https://www.docs.developers.amplitude.com/data/debugger/>. 233 *Ingestion debugger* or via *User lookup* are the most useful options for DDEV. 234 235 Don't forget to select the matching environment while debugging. 236 237 ### Examining data on Amplitude.com 238 239 First, local `ddev` binaries have to be built with `AmplitudeAPIKey` set. Visit `https://app.amplitude.com/data/ddev/DDEV/sources/production` and select either "Production" or "Development", then click the "Go SDK" line to get the API key. Then set `export AmplitudeAPIKey=<key>` and build the binaries with `make`. 240 241 Then run `ddev` commands as usual, and the data will be sent to Amplitude. 242 243 * You can examine data on the local side with `export DDEV_VERBOSE=true` but it's awkward. However, the actual data is always marked with `AMPLITUDE:` and the `EventType` will be `Command`, `Project`, or `$identify` (User data). For example, DDEV_VERBOSE=true ddev start 2>&1 | grep AMPLITUDE` 244 * To see the data show up on Amplitude, you'll need to `ddev debug instrumentation flush`. 245 * To make it easier to find your data, use the "Development" key and set your `instrumentation_user` to a familiar value in `~/.ddev/global_config.yaml`. For example, `instrumentation_user: rfay` would make it so you can find the user `rfay`. 246 * To inspect data, visit "User Lookup", (`https://app.amplitude.com/analytics/ddev/activity`) and choose the correct source in the upper left ("DDEV Production" or "DDEV Development"). Then use "Search users" in the upper right to find the user you are studying. If you've used an `instrumentation_user` it will be searchable as "User". (Advanced->where: "User" = "rfay". for example). You'll then have a page devoted to the events of that user. 247 248 ## Building 249 250 * You'll want both your fork/branch and the upstream as remotes in Git, so that tags can be determined. For example, the upstream Git remote can be `https://github.com/ddev/ddev` and your fork's remote can be `git@github.com:<yourgithubuser>/ddev`. Without the upstream, Git may not know about tags that it needs for tests to work. 251 * To run tests, you'll want `~/tmp` to be allowed in Docker. This is not normally an issue as the home directory is available by default in most Docker providers. 252 253 Build the project with `make` and your resulting executable will end up in `.gotmp/bin/linux_amd64/ddev` or `.gotmp/bin/linux_arm64/ddev` (for Linux) or `.gotmp/bin/windows_amd64/ddev.exe` (for Windows) or `.gotmp/bin/darwin_amd64/ddev` or `.gotmp/bin/darwin_arm64/ddev` (for macOS). 254 255 You can add additional `go build` args with `make BUILDARGS=<something>`, for example, `make BUILDARGS=-race`. 256 257 Build/test/check static analysis with: 258 259 ``` 260 make # Builds on current os/architecture 261 make BUILDARGS=-race 262 make linux_amd64 263 make linux_arm64 264 make darwin_amd64 265 make darwin_arm64 266 make windows_amd64 267 make test 268 make clean 269 make staticrequired 270 ``` 271 272 ## Testing 273 274 Normal test invocation is `make test`. Run a single test with an invocation like `go test -v -run TestDevAddSites ./pkg/...` or `make test TESTARGS="-run TestDevAddSites"`. The easiest way to run tests is from inside the excellent golang IDE [GoLand](https://www.jetbrains.com/go/). Click the arrowhead to the left of the test name. This is also easy to do in Visual Studio Code. 275 276 To test with race detection, `make test TESTARGS="-race"` for example. 277 278 To see which DDEV commands the tests are executing, set the environment variable `DDEV_DEBUG=true`. 279 280 Use `GOTEST_SHORT=true` to run one CMS in each test, or `GOTEST_SHORT=<integer>` to run exactly one project type from the list of project types in the [TestSites array](https://github.com/ddev/ddev/blob/master/pkg/ddevapp/ddevapp_test.go#L43). For example, `GOTEST_SHORT=5 make test TESTARGS="-run TestDdevFullSiteSetup"` will run only `TestDdevFullSiteSetup` against TYPO3. 281 282 To run a test (in the `cmd` package) against a individually-compiled DDEV binary, set the `DDEV_BINARY_FULLPATH` environment variable, for example `DDEV_BINARY_FULLPATH=$PWD/.gotmp/bin/linux_amd64/ddev make testcmd`. 283 284 The easiest way to run tests is using GoLand (or VS Code) with their built-in test runners and debuggers. You can step through a specific test; you can stop at the point before the failure and experiment with the site that the test has set up. 285 286 ## Automated Testing 287 288 Anybody can view the CircleCI automated tests, and they usually show up any problems that are not OS-specific. Click through on the testing section of the PR to see them. 289 290 The Buildkite automated tests require special access, which we typically grant to any PR contributor that asks for it. 291 292 ## Docker Image Development 293 294 The Docker images that DDEV uses are included in the `containers/` directory: 295 296 * `containers/ddev-php-base` the base build for `ddev-webserver`. 297 * `containers/ddev-webserver` provides the web servers for per-project `web` containers. 298 * `containers/ddev-dbserver` provides the `db` container for per-project databases. 299 * `containers/ddev-nginx-proxy-router` is the (deprecated) the nginx-proxy router image. 300 * `containers/ddev-ssh-agent` provides a single in-Docker-network SSH agent so projects can use your SSH keys. 301 * `containers/ddev-traefik-router` is the current Traefik-based router image. 302 303 When changes are made to an image, they have to be temporarily pushed to a tag—ideally with the same as the branch name of the PR—and the tag updated in `pkg/versionconstants/versionconstants.go`. Please ask if you need a container pushed to support a pull request. 304 305 ## Pull Request Pro Tips 306 307 * **[Fork](https://docs.github.com/en/get-started/quickstart/contributing-to-projects) the repository** and clone it locally. Connect your local to the original ‘upstream’ repository by adding it as a remote, and pull upstream changes often so you stay up to date and reduce the likelihood of conflicts when you submit your pull request. See more detailed instructions [here](https://help.github.com/articles/syncing-a-fork). 308 * **Create a [branch](https://docs.github.com/en/get-started/quickstart/github-flow)** for your edits. 309 * **Be clear** about the problem and how someone can recreate it, or why your feature will help. Be equally clear about the steps you took to make your changes. 310 * **It’s best to test**. Run your changes against any existing tests and create new tests when needed. Whether tests exist or not, make sure your changes don’t break the existing project. 311 312 ## Open Pull Requests 313 314 Once you’ve opened a pull request, a discussion will start around your proposed changes. Other contributors and users may chime in, but ultimately the decision is made by the maintainer(s). You may be asked to make some changes to your pull request. If so, add more commits to your branch and push them. They’ll automatically go into the existing pull request. 315 316 If your pull request is merged, great! If not, no sweat; it may not be what the project maintainer had in mind, or they were already working on it. This happens, so our recommendation is to take any feedback you’ve received and go forth and pull request again. Or create your own open source project. 317 318 ### Pull Request Title Guidelines 319 320 We have very precise rules over how our PR titles (and thus master-branch commits) are to be formatted. This leads to **more readable messages** that are easy to follow when looking through the **project history**. But also, we use the master-branch Git commit messages to **generate the changelog** for the releases. 321 322 The pull request title must follow this convention which is based on the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification: 323 324 `<type>[optional !]: <description>[, fixes #<issue>]` 325 326 #### Examples 327 328 * `build: bump mutagen to 0.17.2` 329 * `ci: enforce commit message convention, fixes #5037` 330 * `docs: change code refs of Mac M1 to Apple Silicon` 331 * `feat: allow multiple upload dirs, fixes #4190, fixes #4796` 332 * `fix: create upload_dir if it doesn't exist in ddev composer create, fixes #5031` 333 * `refactor: add new Amplitude Property DDEV-Environment` 334 * `test: optimize caching of downloaded assets` 335 336 #### Type 337 338 Must be one of the following: 339 340 * **build**: Changes that affect the build or external dependencies 341 * **ci**: Changes to our CI configuration files and scripts 342 * **docs**: Documentation only changes 343 * **feat**: A new feature 344 * **fix**: A bugfix 345 * **refactor**: A code change that neither fixes a bug nor adds a feature 346 * **test**: Adding missing tests or correcting existing tests 347 348 Regarding SemVer, all types above except `feat` increase the patch version, `feat` increases the minor version. 349 350 #### Scope 351 352 No scope must be used. 353 354 #### Breaking Changes 355 356 Breaking changes must have a `!` appended after type/scope. 357 358 Regarding SemVer, breaking changes increase the major version. 359 360 #### Subject / Description 361 362 The subject contains a succinct description of the change: 363 364 * use the imperative, present tense: "change" not "changed" nor "changes" 365 * don't capitalize the first letter 366 * no dot (.) at the end 367 368 If an issue exists for the change, `, fixes #<issue number>` must be appended to the subject. 369 370 #### Revert 371 372 If the commit reverts a previous commit, it should begin with `revert:`, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted. 373 374 ## Coding Style 375 376 Unless explicitly stated, we follow all coding guidelines from the Go community. While some of these standards may seem arbitrary, they somehow seem to result in a solid, consistent codebase. 377 378 It is possible that the codebase does not currently comply with these guidelines. We are not looking for a massive PR that fixes this since that goes against the spirit of the guidelines. All new contributions should make a best effort to clean up and make the codebase better than they left it. Obviously, apply your best judgment. Remember, the goal here is to make the codebase easier for humans to navigate and understand. Always keep that in mind when nudging others to comply. 379 380 Use `make staticrequired` to ensure that your code can pass the required static analysis tests. 381 382 The rules: 383 384 1. All code should be formatted with `gofmt -s`. 385 2. All code should pass the default levels of [`golint`](https://github.com/golang/lint). 386 3. All code should follow the guidelines covered in [Effective Go](http://golang.org/doc/effective_go.html) and [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments). 387 4. Comment the code. Tell us the why, the history and the context. 388 5. Document *all* declarations and methods, even private ones. Declare expectations, caveats and anything else that may be important. If a type gets exported, having the comments already there will ensure it’s ready. 389 6. Variable name length should be proportional to its context and no longer. `noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`. In practice, short methods will have short variable names and globals will have longer names. 390 7. No underscores in package names. If you need a compound name, step back, and re-examine why you need a compound name. If you still think you need a compound name, lose the underscore. 391 8. All tests should run with `go test` and outside tooling should not be required. No, we don’t need another unit testing framework. Assertion packages are acceptable if they provide *real* incremental value. 392 9. Even though we call these “rules” above, they are guidelines. Since you’ve read all the rules, you now know that. 393 394 If you are having trouble getting into the mood of idiomatic Go, we recommend reading through [Effective Go](https://golang.org/doc/effective_go.html). The [Go Blog](https://blog.golang.org) is also a great resource. Drinking the kool-aid is a lot easier than going thirsty.