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  [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](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.