get.porter.sh/porter@v1.3.0/CONTRIBUTING.md (about)

     1  # Contributing Guide
     2  
     3  ---
     4  * [How to help](#how-to-help)
     5    * [Code of Conduct](#code-of-conduct)
     6    * [Find an issue](#find-an-issue)
     7    * [Which branch to use](#which-branch-to-use)
     8    * [When to open a pull request](#when-to-open-a-pull-request)
     9    * [How to test your pull request](#how-to-test-your-pull-request)
    10    * [How to get your pull request reviewed fast](#how-to-get-your-pull-request-reviewed-fast)
    11    * [Signing your commits](#signing-your-commits)
    12    * [The life of a pull request](#the-life-of-a-pull-request)
    13  * [Contribution Ladder](#contribution-ladder)
    14  * [Developer Tasks](#developer-tasks)
    15    * [Initial setup](#initial-setup)
    16    * [Magefile explained](#magefile-explained)
    17    * [Test Porter](#test-porter)
    18    * [Install mixins](#install-mixins)
    19    * [Plugin Debugging](#plugin-debugging)
    20    * [Preview documentation](#preview-documentation)
    21    * [Write a blog post](#write-a-blog-post)
    22    * [View a trace of a Porter command](#view-a-trace-of-a-porter-command)
    23    * [Debug Smoke Tests](#debug-smoke-tests)
    24    * [Command Documentation](#command-documentation)
    25    * [Work on the Porter Operator](#work-on-the-porter-operator)
    26  * [Code structure and practices](#code-structure-and-practices)
    27    * [What is the general code layout?](#what-is-the-general-code-layout)
    28    * [Logging](#logging)
    29      * [Tracing Sensitive Data](#tracing-sensitive-data)
    30    * [Breaking Changes](#breaking-changes)
    31  * [Infrastructure](#infrastructure)
    32    * [CDN Setup](#cdn-setup)
    33    * [Custom Windows CI Agent](#custom-windows-ci-agent)
    34    * [Releases](#releases)
    35  ---
    36  
    37  ## How to help
    38  
    39  We welcome your contributions and participation! If you aren't sure what to
    40  expect, here are some norms for our project so you feel more comfortable with
    41  how things will go.
    42  
    43  If this is your first contribution to Porter, we have a [tutorial] that walks you
    44  through how to setup your developer environment, make a change and test it.
    45  
    46  [tutorial]: https://porter.sh/docs/contribute/tutorial/
    47  
    48  ### Code of Conduct
    49  
    50  The Porter community is governed by our [Code of Conduct][coc].
    51  This includes but isn't limited to: the porter and related mixin repositories,
    52  slack, interactions on social media, project meetings, conferences and meetups.
    53  
    54  [coc]: https://porter.sh/src/CODE_OF_CONDUCT.md
    55  
    56  ### Find an issue
    57  
    58  Use the [porter.sh/find-issue] link to find good first issues for new contributors and help wanted issues for our other contributors.
    59  
    60  When you have been contributing for a while, take a look at the "Backlog" column on our [project board][board] for high priority issues.
    61  The project board is at the organization level, so it contains issues from across all the Porter repositories. 
    62  
    63  * [`good first issues`][good-first-issue] has extra information to help you make your first contribution.
    64  * [`help wanted`][help-wanted] are issues suitable for someone who isn't a core maintainer.
    65  * `hmm 🛑🤔` issues should be avoided. They are not ready to be worked on yet
    66    because they are not finished being designed or we aren't sure if we want the
    67    feature, etc.
    68  
    69  Maintainers will do their best to regularly make new issues for you to solve and then 
    70  help out as you work on them. 💖
    71  
    72  We have a [roadmap] that will give you a good idea of the
    73  larger features that we are working on right now. That may help you decide what
    74  you would like to work on after you have tackled an issue or two to learn how to
    75  contribute to Porter. If you have a big idea for Porter, learn [how to propose
    76  a change to Porter][pep].
    77  
    78  Another great way to contribute is to create a mixin! You can start using the
    79  [Porter Skeletor][skeletor] repository as a template to start, along with the
    80  [Mixin Developer Guide][mixin-dev-guide].
    81  
    82  When you create your first pull request, add your name to the bottom of our 
    83  [Contributors][contributors] list. Thank you for making Porter better! 🙇‍♀️
    84  
    85  [porter.sh/find-issue]: https://porter.sh/find-issue/
    86  [contributors]: https://porter.sh/src/CONTRIBUTORS.md                                          
    87  [skeletor]: https://github.com/getporter/skeletor
    88  [mixin-dev-guide]: https://porter.sh/mixin-dev-guide/
    89  [good-first-issue]: https://porter.sh/board/good+first+issue
    90  [help-wanted]: https://porter.sh/board/help+wanted
    91  [board]: https://porter.sh/board
    92  [slack]: https://porter.sh/community#slack
    93  [roadmap]: https://porter.sh/src/README.md#roadmap
    94  [pep]: https://porter.sh/docs/contribute/proposals/
    95  
    96  ### Which branch to use
    97  
    98  Unless the issue specifically mentions a branch, please create your feature branch from the **main** branch.
    99  
   100  For example:
   101  
   102  ```bash
   103  # Make sure you have the most recent changes to main
   104  git checkout main
   105  git pull
   106  
   107  # Create a branch based on main named MY_FEATURE_BRANCH
   108  git checkout -b MY_FEATURE_BRANCH main
   109  ```
   110  
   111  ### When to open a pull request
   112  
   113  It's OK to submit a PR directly for problems such as misspellings or other
   114  things where the motivation/problem is unambiguous.
   115  
   116  If there isn't an issue for your PR, please make an issue first and explain the
   117  problem or motivation for the change you are proposing. When the solution isn't
   118  straightforward, for example, "Implement missing command X", then also outline
   119  your proposed solution. Your PR will go smoother if the solution is agreed upon
   120  before you've spent a lot of time implementing it.
   121  
   122  Since Porter is a CLI, the "solution" will usually look like this:
   123  
   124  ```console
   125  $ porter newcommand [OPTIONAL] [--someflag VALUE]
   126  example output
   127  ```
   128  
   129  ### How to test your pull request
   130  
   131  We recommend running the following every time:
   132  
   133  ```
   134  mage Build TestUnit
   135  ```
   136  
   137  If your test modified anything related to running a bundle, also run:
   138  
   139  ```
   140  mage TestIntegration
   141  ```
   142  
   143  If you want to know _all_ the targets that the CI runs, look at
   144  <build/azure-pipelines.pr-automatic.yml>.
   145  
   146  ### How to get your pull request reviewed fast
   147  
   148  🚧 If you aren't done yet, create a draft pull request or put WIP in the title
   149  so that reviewers wait for you to finish before commenting.
   150  
   151  1️⃣ Limit your pull request to a single task. Don't tackle multiple unrelated
   152  things, especially refactoring. If you need large refactoring for your change,
   153  chat with a maintainer first, then do it in a separate PR first without any
   154  functionality changes.
   155  
   156  🎳 Group related changes into separate commits to make it easier to review. 
   157  
   158  😅 Make requested changes in new commits. Please don't amend or rebase commits
   159  that we have already reviewed. When your pull request is ready to merge, you can
   160  rebase your commits yourself, or we can squash when we merge. Just let us know
   161  what you are more comfortable with.
   162  
   163  🚀 We encourage [follow-on PRs](#follow-on-pr) and a reviewer may let you know in
   164  their comment if it is okay for their suggestion to be done in a follow-on PR.
   165  You can decide to make the change in the current PR immediately, or agree to
   166  tackle it in a reasonable amount of time in a subsequent pull request. If you
   167  can't get to it soon, please create an issue and link to it from the pull
   168  request comment so that we don't collectively forget.
   169  
   170  ### Signing your commits
   171  
   172  You can automatically sign your commits to meet the DCO requirement for this
   173  project by running the following command: `mage SetupDCO`.
   174  
   175  Licensing is important to open source projects. It provides some assurances that
   176  the software will continue to be available based under the terms that the
   177  author(s) desired. We require that contributors sign off on commits submitted to
   178  our project's repositories. The [Developer Certificate of Origin
   179  (DCO)](https://developercertificate.org/) is a way to certify that you wrote and
   180  have the right to contribute the code you are submitting to the project.
   181  
   182  You sign-off by adding the following to your commit messages:
   183  
   184  ```bash
   185  Author: Your Name <your.name@example.com>
   186  Date:   Thu Feb 2 11:41:15 2018 -0800
   187  
   188      This is my commit message
   189  
   190      Signed-off-by: Your Name <your.name@example.com>
   191  ```
   192  
   193  Notice the `Author` and `Signed-off-by` lines match. If they don't, the PR will
   194  be rejected by the automated DCO check.
   195  
   196  Git has a `-s` command line option to do this automatically:
   197  
   198  ```
   199  git commit -s -m 'This is my commit message'
   200  ```
   201  
   202  If you forgot to do this and have not yet pushed your changes to the remote
   203  repository, you can amend your commit with the sign-off by running 
   204  
   205  ```
   206  git commit --amend -s
   207  ```
   208  
   209  ### The life of a pull request
   210  
   211  1. You create a draft or WIP pull request. Reviewers will ignore it mostly
   212     unless you mention someone and ask for help. Feel free to open one and use
   213     the pull request to see if the CI passes. Once you are ready for a review,
   214     remove the WIP or click "Ready for Review" and leave a comment that it's
   215     ready for review.
   216  
   217     If you create a regular pull request, a reviewer won't wait to review it.
   218  1. A reviewer will assign themselves to the pull request. If you don't see
   219     anyone assigned after 3 business days, you can leave a comment asking for a
   220     review, or ping in [slack][slack]. Sometimes we have busy days, sick days,
   221     weekends and vacations, so a little patience is appreciated! 🙇‍♀️
   222  1. The reviewer will leave feedback.
   223      * `nits`: These are suggestions that you may decide to incorporate into your pull
   224        request or not without further comment.
   225      * It can help to put a 👍 on comments that you have implemented so that you
   226        can keep track.
   227      * It is okay to clarify if you are being told to make a change or if it is a
   228        suggestion.
   229  1. After you have made the changes (in new commits please!), leave a comment. If
   230     3 business days go by with no review, it is okay to bump.
   231  1. When a pull request has been approved, the reviewer will squash and merge
   232     your commits. If you prefer to rebase your own commits, at any time leave a
   233     comment on the pull request to let them know that.
   234  
   235  At this point your changes are available in the [canary][canary] release of
   236  Porter! After your first pull request is merged, you will be invited to the
   237  [Contributors team] which you may choose to accept (or not). Joining the team lets
   238  you have issues in GitHub assigned to you.
   239  
   240  [canary]: https://porter.sh/install/#canary
   241  [Contributors team]: https://github.com/orgs/getporter/teams/contributors
   242  
   243  #### Follow-on PR
   244  
   245  A follow-on PR is a pull request that finishes up suggestions from another pull
   246  request.
   247  
   248  When the core of your changes are good, and it won't hurt to do more of the
   249  changes later, our preference is to merge early, and keep working on it in a
   250  subsequent. This allows us to start testing out the changes in our canary
   251  builds, and more importantly enables other developers to immediately start
   252  building their work on top of yours.
   253  
   254  This helps us avoid pull requests to rely on other pull requests. It also avoids
   255  pull requests that last for months, and in general we try to not let "perfect be
   256  the enemy of the good". It's no fun to watch your work sit in purgatory, and it
   257  kills contributor momentum.
   258  
   259  ## Contribution Ladder
   260  
   261  Our [contribution ladder][ladder] defines the roles and responsibilities for this
   262  project and how to participate with the goal of moving from a user to a
   263  maintainer.
   264  
   265  [ladder]: https://porter.sh/src/CONTRIBUTION_LADDER.md
   266  
   267  ## Developer Tasks
   268  
   269  ### Initial setup
   270  
   271  We have a [tutorial] that walks you through how to set up your developer
   272  environment, make a change and test it.
   273  
   274  Here are the key steps, if you run into trouble, the tutorial has more details:
   275  
   276  1. Install Go version 1.17 or higher.
   277  1. Clone this repository with `git clone https://github.com/getporter/porter.git ~/go/src/get.porter.sh/porter`.
   278  1. Run `go run mage.go EnsureMage` to install [mage](#magefile-explained).
   279  1. Run `mage Build Install` from within the newly cloned repository.
   280  
   281  If you are planning on contributing back to the project, you'll need to
   282  [fork](https://guides.github.com/activities/forking/) and clone your fork. If
   283  you want to build porter from scratch, you can follow the process above and
   284  clone directly from the project.
   285  
   286  You now have canary builds of porter and all the mixins installed.
   287  
   288  ### Magefile explained
   289  
   290  Porter uses a cross-platform make alternative called [mage](https://magefile.org), where the targets are written in Go.
   291  
   292  #### Mage Targets
   293  
   294  Mage targets are not case-sensitive, but in our docs we use camel case to make
   295  it easier to read. You can run either `mage TestSmoke` or `mage testsmoke` for
   296  example.
   297  
   298  * **Build** builds all binaries, porter and internal mixins.
   299    * **BuildClient** just builds the porter client for your operating system.
   300      It does not build the porter-runtime binary. Useful when you just want to do a
   301      build and don't remember the proper way to call `go build` yourself.
   302    * **BuildPorter**     builds both the porter client and runtime.
   303  * **Clean** removes artifacts from previous builds and test runs.
   304  * **UpdateTestfiles** updates the "golden" test files to match the latest test output.
   305    This is mostly useful for when you change the schema of porter.yaml which will
   306    break TestPorter_PrintManifestSchema. Run this target to fix it.
   307    Learn more about [golden files].
   308  * **Test** runs all the tests.
   309    * **TestUnit** runs the unit tests
   310    * **TestSmoke** runs a small suite of tests using the Porter CLI to validate
   311      that Porter is (mostly) working.
   312    * **TestIntegration** runs our integration tests, which run the bundles
   313      against a test KIND cluster.
   314  * **Install** installs porter _and_ the mixins from source into **$(HOME)/.porter/**.
   315  * **DocsPreview** hosts the docs site. See [Preview Documentation](#preview-documentation).
   316  * **DocsGen** generates the CLI documentation for the website. This is run automatically by build.
   317  * **SetupDCO** installs a git commit hook that automatically signsoff your commit
   318    messages per the DCO requirement.
   319  
   320  [golden files]: https://ieftimov.com/post/testing-in-go-golden-files/
   321  
   322  ### Test Porter
   323  
   324  We have a few different kinds of tests in Porter. You can run all tests types
   325  with `mage test`.
   326  
   327  #### Unit Tests
   328   
   329  ```
   330  mage TestUnit
   331  ```
   332  
   333  Should not rely on Docker, or try to really run bundles without key components
   334  mocked. Most structs have test functions, e.g. `porter.NewTestPorter` that are
   335  appropriate for unit tests.
   336  
   337  Fast! 🏎💨 This takes about 15s - 3 minutes, depending on your computer hardware.
   338  
   339  #### Integration Tests
   340  
   341  ```
   342  mage TestIntegration
   343  ```
   344  
   345  These tests run parts of Porter, using the Porter structs instead of the cli.
   346  They can use Docker, expect that a cluster is available, etc. These tests all
   347  use functions like `porter.SetupIntegrationTest()` to update the underlying
   348  components so that they hit the real filesystem, and don't mock out stuff like
   349  Docker.
   350  
   351  You must have Docker on your computer to run these tests. The test setup handles
   352  creating a Kubernetes cluster and Docker registry. Since they are slow, it is
   353  perfectly fine to not run these locally and rely on the CI build that's triggered
   354  when you push commits to your pull request instead.
   355  
   356  When I am troubleshooting an integration test, I will run just the single test
   357  locally by using `go test -run TESTNAME ./...`. If the test needs infrastructure, 
   358  we have scripts that you can use, like `mage StartDockerRegistry` or 
   359  `mage EnsureTestCluster`.
   360  
   361  Slow! 🐢 This takes between 8-16 minutes, depending on your computer hardware.
   362  
   363  🚧 We are in the process of updating our integration tests to reduce total test time by combining test cases into larger tests so that we aren't running expensive set up operations like building bundles as often.
   364  The goal is to make the integration tests use the same test framework [tests/tester] which tests porter using the CLI instead of by calling porter's code directly.
   365  Follow the example of the [hello smoke test] for a good test to copy and make new tests from.
   366  We use the [Testify library](https://github.com/stretchr/testify)'s [`require`](https://pkg.go.dev/github.com/stretchr/testify@v1.8.2/require) package (instead of [`assert`](https://pkg.go.dev/github.com/stretchr/testify@v1.8.2/assert)) in these long tests because we want the test to stop on the first failure instead of having the rest of the test output a bunch of failures too.
   367  The first failure is the relevant one, and stopping immediately makes it faster and easier to see the root problem.
   368  
   369  [hello smoke test]: https://github.com/getporter/porter/blob/main/tests/smoke/hello_test.go
   370  [tests/tester]: https://github.com/getporter/porter/blob/main/tests/tester/main.go#L46
   371  
   372  #### Smoke Tests
   373  
   374  ```
   375  mage testSmoke
   376  ```
   377  
   378  Smoke tests test Porter using the CLI and quickly identify big problems with a
   379  build that would make it unusable.
   380  If you intend to change Porter code (not docs), we recommend running smoke tests locally before submitting a pull request with your changes. Doing so allows you to immediately ensure you haven't broken or altered any core functionality.
   381  
   382  Short! We want this to always be something you can run in under 3 minutes.
   383  
   384  ### Install mixins
   385  
   386  When you run `mage build`, the canary\* build of mixins are automatically
   387  installed into your bin directory in the root of the repository. You can use
   388  `porter mixin install NAME` to install the latest released version of a mixin.
   389  
   390  \* canary = most recent successful build of the "main" branch
   391  
   392  ### Plugin Debugging
   393  
   394  If you are developing a [plugin](https://porter.sh/plugins/) and you want to
   395  debug it follow these steps:
   396  
   397  The plugin to be debugged should be compiled and placed in porters plugin path
   398  (e.g. in the Azure plugin case the plugin would be copied to
   399  $PORTER_HOME/plugins/azure/.
   400  
   401  The following environment variables should be set:
   402  
   403  `PORTER_RUN_PLUGIN_IN_DEBUGGER` should be set to the name of the plugin to be
   404  debugged (e.g. secrets.azure.keyvault to debug the azure secrets plugin)  
   405  `PORTER_DEBUGGER_PORT` should be set to the port number where the delve API will
   406  listen, if not set it defaults to 2345  
   407  `PORTER_PLUGIN_WORKING_DIRECTORY` should be the path to the directory containing
   408  *source code* for the plugin being executed.  
   409  
   410  When porter is run it will start delve and attach it to the plugin process, this
   411  exposes the delve API so that any delve client can connect to the server and
   412  debug the plugin.
   413  
   414  ### Preview documentation
   415  
   416  We use [Hugo](https://gohugo.io) to build our documentation site, and it is hosted on
   417  [Netlify](https://netlify.com). You don't have to install Hugo locally because the
   418  preview happens inside a docker container.
   419  
   420  1. Run `mage DocsPreview` to start serving the docs. It will watch the file
   421  system for changes.
   422  1. Our mage target should open <http://localhost:1313/docs> to preview the
   423  site/docs.
   424  
   425  or use only Hugo
   426  
   427  1. Download and install [Hugo 0.117.0](https://github.com/gohugoio/hugo/releases/tag/v0.117.0) extended version. 
   428  2. `cd docs`
   429  3. `hugo server --watch`
   430  
   431  We welcome your contribution to improve our documentation, and we hope it is an
   432  easy process! ❤️
   433  
   434  ### Write a blog post
   435  
   436  Thank you for writing a post for our blog! 🙇‍♀️ Here's what you need to do to create
   437  a new blog post and then preview it:
   438  
   439  1. Go to /docs/content/blog and create a new file. Whatever you name the file
   440      will be the last part of the URL. For example a file named
   441      "porter-collaboration.md" will be located at
   442      <https://porter.sh/blog/porter-collaboration/>.
   443      
   444  1. At the top of the file copy and paste the frontmatter template below. The
   445      frontmatter is YAML that instructs the blogging software, Hugo, how to render the
   446      blog post.
   447      
   448      ```yaml
   449     ---
   450     title: "Title of Your Blog Post in Titlecase"
   451     description: "SEO description of your post, displayed in search engine results."
   452     date: "2020-07-28"
   453     authorname: "Your Name"
   454     author: "@yourhandle" #Not used to link to github/twitter, but informally that's what people put here
   455     authorlink: "https://link/to/your/website" # link to your personal website, github, social media...
   456     authorimage: "https://link/to/your/profile/picture" # Optional, https://github.com/yourhandle.png works great
   457     tags: [] # Optional, look at other pages and pick tags that are already in use, e.g. ["mixins"]
   458     ---
   459     ```
   460  
   461  1. [Preview](#preview-documentation) the website and click "Blog" at the top 
   462      right to find your blog post.
   463  
   464  1. When you create a pull request, look at the checks run by the pull request,
   465      and click "Details" on the **netlify/porter/deploy-preview** one to see a live
   466      preview of your pull request.
   467      
   468  Our pull request preview and the live site will not show posts with a date in
   469  the future. If you don't see your post, change the date to today's date.
   470  
   471  ### View a trace of a Porter command
   472  
   473  Porter can send trace data about the commands run to an OpenTelemetry backend.
   474  It can be very helpful when figuring out why a command failed because you can see the values of variables and stack traces.
   475  
   476  In development, you can use the [otel-jaeger bundle] to set up a development instance of Jaeger, which gives you a nice website to see each command run.
   477  
   478  ```
   479  porter install OtelJaeger --reference ghcr.io/getporter/examples/otel-jaeger:v0.1.0 --allow-docker-host-access
   480  ```
   481  
   482  Then to turn on tracing in Porter, set the following environment variables.
   483  This tells Porter to turn on tracing, and connect to OpenTelemetry server that you just installed.
   484  
   485  **Posix**
   486  ```bash
   487  export PORTER_TELEMETRY_ENABLED="true"
   488  export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
   489  export OTEL_EXPORTER_OTLP_INSECURE="true"
   490  ```
   491  
   492  **Powershell**
   493  ```powershell
   494  $env:PORTER_TELEMETRY_ENABLED="true"
   495  $env:OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
   496  $env:OTEL_EXPORTER_OTLP_INSECURE="true"
   497  ```
   498  
   499  Next run a Porter command to generate some trace data, such as `porter list`.
   500  Then go to the Jaeger website to see your data: http://localhost:16686.
   501  On the Jaeger dashboard, select "porter" from the service drop down, and click "Find Traces".
   502  
   503  The smoke and integration tests will run with telemetry enabled when the PORTER_TEST_TELEMETRY_ENABLED environment variable is true.
   504  
   505  [otel-jaeger bundle]: https://porter.sh/examples/src/otel-jaeger
   506  
   507  ### Debug Smoke Tests
   508  
   509  If you want to attach a debugger to Porter when it is running in a smoke test, first install delve:
   510  
   511  ```
   512  go install github.com/go-delve/delve/cmd/dlv@latest
   513  ```
   514  
   515  Next, determine the porter command that you want the smoke test to automatically run through delve so that you can attach to it and debug.
   516  Set `PORTER_RUN_IN_DEBUGGER` to the command(s) to run in delve.
   517  Use `porter` to target any porter command, or target a specific command for example `porter installation apply`.
   518  
   519  When the smoke tests run a porter command that has the prefix contained in PORTER_RUN_IN_DEBUGGER, instead of running porter directly, porter is run in delve.
   520  Delve will wait for you to attach a debugger, and then proceed to run the porter command so that you can debug into it.
   521  
   522  The default debugger port is `55942` which you can override with the `PORTER_DEBUGGER_PORT` environment variable.
   523  
   524  Now run the smoke test with `go test -run TESTNAME -tags smoke ./tests/smoke`, then use your Go IDE or delve directly to attach to Porter.
   525  If you are using GoLand, use the **Go Remote** debug configuration and make sure to specify the same port that you used when running the smoke test (default is 55942).
   526  
   527  ### Command Documentation
   528  
   529  Our commands are documented at <https://porter.sh/docs/references/> and that documentation is
   530  generated by our CLI. You should regenerate that documentation when you change
   531  any files in **cmd/porter** by running `mage DocsGen` which is run every time
   532  you run `mage build`.
   533  
   534  ### Work on the Porter Operator
   535  
   536  Instructions for building the Porter Operator from source are located in its repository: https://github.com/getporter/operator.
   537  Sometimes you may need to make changes to Porter and work on the Operator at the same time.
   538  Here's how to build porter so that you can use it locally:
   539  
   540  1. You must be on a feature branch. Not main. This matters because it affects the generated
   541     docker image tag.
   542  1. Deploy the operator to a KinD cluster by running `mage deploy` from inside the operator repository.
   543     That cluster has a local registry running that you can publish to, and it will pull images from it, 
   544     running on localhost:5000.
   545  1. Run the following command from the porter repository to build the Porter Agent image, and publish it
   546     to the test cluster's registry. `mage XBuildAll LocalPorterAgentBuild`.
   547  1. Edit your AgentConfig in the Porter Operator and set it to use your local build of the porter-agent.
   548  
   549  ```yaml
   550  apiVersion: porter.sh/v1
   551  kind: AgentConfig
   552  metadata:
   553    name: porter
   554    namespace: test # You may need to change this depending on what you are testing
   555  spec:
   556    porterRepository: localhost:5000/porter-agent
   557    porterVersion: canary-dev
   558    serviceAccount: porter-agent
   559  ```
   560  
   561  ## Code structure and practices
   562  
   563  Carolyn Van Slyck gave a talk about the design of Porter, [Designing
   564  Command-Line Tools People Love][porter-design] that you may find helpful in
   565  understanding the why's behind its command grammar, package structure, use of
   566  dependency injection and testing strategies.
   567  
   568  [porter-design]: https://carolynvanslyck.com/talks/#gocli
   569  
   570  ### What is the general code layout?
   571  
   572  * **cmd**: go here to add a new command or flag to porter or one of the mixins in
   573    this repository
   574  * **docs**: our website
   575  * **pkg**
   576    * **build**: implements building the bundle image.
   577    * **cache**: handles the cache of bundles that have been pulled by commands
   578    like `porter install --reference`.
   579    * **cnab**: deals with the CNAB spec
   580      * **cnab-to-oci**: talking to an OCI registry.
   581      * **config-adapter**: converting porter.yaml to bundle.json.
   582      * **extensions**: extensions to the CNAB spec, at this point that's just
   583    dependencies.
   584      * **provider**: the CNAB runtime, i.e. `porter install`.
   585    * **config**: anything related to `porter.yaml` and `~/.porter`.
   586    * **context**: essentially dependency injection that's needed throughout Porter,
   587      such as stdout, stderr, stdin, filesystem and command execution.
   588    * **exec**: the exec mixin
   589    * **mixin**: enums, functions and interfaces for the mixin framework.
   590      * **feed**: works with mixin atom feeds
   591      * **provider**: handles communicating with mixins
   592    * **porter**: the implementation of the porter commands. Every command in Porter
   593      has a corresponding function in here.
   594      * **version**: reusable library used by all the mixins for implementing a mixin
   595    * **secrets**: used to access porter's secret store through plugins.
   596    * **storage**: used to access porter's data store through plugins.
   597    * **templates**: files that need to be compiled into the porter binary with
   598        version command.
   599  * **scripts**:
   600    * **install**: Porter [installation](https://porter.sh/install) scripts
   601  * **tests** have Go-based integration tests.
   602  
   603  ### Logging
   604  
   605  **Print to the `Out` property for informational messages and send debug messages to the `Err` property.**
   606  
   607  Example:
   608  
   609  ```golang
   610  fmt.Fprintln(p.Out, "Initiating battlestar protocol")
   611  fmt.Fprintln(p.Err, "DEBUG: loading plans from r2d2...")
   612  ```
   613  
   614  Most of the structs in Porter have an embedded
   615  `get.porter.sh/porter/pkg/portercontext.Context` struct. This has both `Out` and
   616  `Err` which represent stdout and stderr respectively. You should log to those
   617  instead of directly to stdout/stderr because that is how we capture output in
   618  our unit tests. That means use `fmt.Fprint*` instead of `fmt.Print*` so that you
   619  can pass in `Out` or `Err`.
   620  
   621  Some of our commands are designed to be consumed by another tool and intermixing
   622  debug lines and the command output would make the resulting output unusable. For
   623  example, `porter schema` outputs a json schema and if log lines were sent to
   624  stdout as well, then the resulting json schema would be unparsable. This is why
   625  we send regular command output to `Out` and debug information to `Err`. It
   626  allows us to then run the command and see the debug output separately, like so
   627  `porter schema --debug 2> err.log`.
   628  
   629  #### Tracing Sensitive Data
   630  
   631  Sometimes when debugging your code you may need to print out variables that can contain sensitive data, for example printing out the resolved values for parameters and credentials.
   632  In this case, do not use fmt.Println and instead use the open telemetry trace logger to include the data in attributes.
   633  
   634  ```go
   635  func myFunc(ctx context.Context) {
   636    ctx, span := tracing.StartSpan(ctx)
   637    defer span.EndSpan()
   638    
   639    // This contains resolved sensitive values, so only trace it in special dev builds (nothing is traced for release builds)
   640    span.SetSensitiveAttributes(attribute.String("sensitive-stuff", mysensitiveVar))
   641  }
   642  ```
   643  
   644  In normal builds of Porter, created with `go build`, `mage Build` or `mage XBuildAll`, no sensitive data is ever traced because `SetSensitiveAttributes` is compiled to do nothing by default. 
   645  Calls to `SetSensitiveAttributes` are only implemented when Porter is built with the `traceSensitiveAttributes` build tag.
   646  
   647  Each time you call `SetSensitiveAttributes` include in your comment why the data is sensitive, and that it is only traced in special dev builds since people may not think to read the function documentation.
   648  
   649  In order to debug your code and see the sensitive data, you need to:
   650  
   651  1. Compile a build of porter with sensitive tracing turned on, `go build -tags traceSensitiveAttributes -o bin/porter ./cmd/porter`.
   652  2. [Enable tracing with Jaeger](#view-a-trace-of-a-porter-command)
   653  3. Run a porter command and then view the sensitive attributes in the trace data sent to Jaeger.
   654  
   655  ### Breaking Changes
   656  
   657  Some changes in Porter break our compatibility with previous versions of Porter.
   658  When that happens, we need to release that change with a new major version number to indicate to users that it contains breaking changes.
   659  When you realize that you may need to make a breaking change, discuss it with a maintainer on the issue or pull request and we'll come up with a plan for how it should be released.
   660  Here are some examples of breaking changes:
   661  
   662  * The schema of porter.yaml changed.
   663  * The schema of Porter's [file formats](https://porter.sh/references/file-formats) changed.
   664  * The schema of Porter's [config file](https://porter.sh/configuration/#config-file) changed.
   665  * Flags or behavior of a CLI command changed, such as removing a flag or adding a validation that can result in a hard error, preventing the command from running.
   666  
   667  All of Porter's documents have a schemaVersion field and when the schema of the document is changed, the version number should be incremented as well in the default set on new documents, the supported schema version constant in the code, and in the documentation for that document.
   668  
   669  ## Infrastructure
   670  
   671  This section includes overviews of infrastructure Porter relies on, mostly intended
   672  for maintainers.
   673  
   674  ### CDN Setup
   675  
   676  See the [CDN Setup Doc][cdn] for details on the services Porter uses to
   677  host and distribute its release binaries.
   678  
   679  ### Custom Windows CI Agent
   680  
   681  Some of our tests need to run on Windows, like the Smoke Tests - Windows stage of our build pipeline.
   682  We use a custom Windows agent registered with Azure Pipelines that we build and maintain ourselves.
   683  See the [Custom Windows CI Agent] documentation for details on how the agent is created and configured.
   684  
   685  ### Releases
   686  
   687  Our [version strategy] explains how we version the project, when you should expect
   688  breaking changes in a release, and the process for the v1 release.
   689  
   690  [cdn]: https://porter.sh/src/infra/cdn.md
   691  [version strategy]: https://porter.sh/references/version-strategy/
   692  [Custom Windows CI Agent]: https://porter.sh/src/infra/custom-windows-ci-agent.md