Contributing Tutorial
We are excited to have you join the Porter community! This tutorial will walk you through how to modify Porter and try out your changes. We encourage you to follow the tutorial before submitting your first pull request because this will help you get your environment set up and become familiar with how to make a change and test it out.
If you run into any trouble, please let us know! The best way to ask for help is to start a new discussion on our forum.
Try Porter
Before contributing to any open-source project, you should start with becoming familiar with the software first.
- Install Porter
- Run through the QuickStart
After this you should be able to answer:
- What is Porter?
- What is a bundle?
- How do I make a bundle?
- How do I install a bundle?
We are always iterating on our documentation. If something isn’t clear and you have read our documentation, let us know by opening an issue. Say what you were trying to learn about, where you looked, if you could find a relevant page, and what still isn’t clear. We can then answer your question and improve our docs so the next person has a better experience.
Setup Environment
First let’s get your computer setup so that you can work on Porter. You will need a few things.
We are improving our support for building Porter on Windows. In a few weeks, we will have it all working on any Windows shell. For now, if you are on Windows, please use Windows Subsystem for Linux (WSL). The rest of this tutorial assumes that you are inside your WSL distribution when installing prerequisites and executing the commands.
-
If you are new to Git or GitHub, we recommend reading the GitHub Guides. They will walk you through installing Git, forking a repository and submitting a pull request.
-
Go version 1.16 or higher
-
Make. You can install it with a package manager such as apt-get, or homebrew.
Add GOPATH/bin to your PATH
Porter relies on a few tools that will be installed in your $GOPATH/bin directory. When go tools are installed, they automatically are put into GOPATH/bin and in order to use them that directory needs to be included in your PATH environment variable. This is a standard Go developer environment configuration and will be useful for other Go projects.
-
Open your shell profile file* and add the following line to the file:
Posix shells like bash and zsh
export PATH=$(go env GOPATH)/bin:$PATH
PowerShell
$env:PATH="$(go env GOPATH)\bin;$env:PATH"
-
Now load the changes to your bash profile with the source command or open a new terminal.
Posix shells like bash and zsh
Replace PROFILE_PATH with the path to your profile
source PROFILE_PATH
PowerShell
. $profile
* There are a bunch of different shell profiles depending on your shell and customizations. The default locations are:
- ~/.bash_profile or ~/.bashrc
- ~/.zshrc
- PowerShell doesn’t have a profile by default. Here’s how to find or create a PowerShell profile.
Checkout Code
Porter can either be cloned into your GOPATH (usually ~/go) or anywhere on your computer. Porter has many repositories. If you decide to not clone Porter into your GOPATH, you may still want to clone Porter and its other repositories into a dedicated directory so they are easier to find later.
git clone https://github.com/getporter/porter.git ~/go/src/get.porter.sh/porter
If you are planning on contributing back to the project, you’ll need to
fork and fetch your fork. Here
is a suggested setup for managing your fork, that uses upstream
to refer to
the original porter repository and origin
to refer to your fork:
-
Fork the repository in GitHub and copy your fork’s reference.
-
In a terminal, cd to where you cloned Porter.
-
Rename the origin remote to upstream:
git remote rename origin upstream
-
Add a remote to your local checkout of Porter for your fork:
git remote add origin https://github.com/YOURNAME/porter.git
-
Run
git remote -vv
to verify that origin is your fork and upstream is the canonical repository.$ git remote -vv origin https://github.com/YOURNAME/porter.git (fetch) origin https://github.com/YOURNAME/porter.git (push) upstream https://github.com/getporter/porter.git (fetch) upstream https://github.com/getporter/porter.git (push)
-
Create a branch for your changes:
git checkout -b YOURBRANCH main
-
Push your branch to your fork (origin):
git push -u origin YOURBRANCH
Afterwards you can use just
git push
to synchronize your local branch with your fork on GitHub. -
Run
git branch -vv
to verify that themain
branch is synchronized withupstream/main
and your branch is synchronized withorigin/YOURBRANCH
.$ git branch -vv * mybranch 26d8358f [origin/mybranch] Review feedback main 7e120aab [upstream/main] Bump cnab-go
Install Mage
We are transitioning from Make to Mage. Installing mage isn’t strictly required,
you can always run go run mage.go TARGET
instead of mage TARGET
. However, having
the tool saves typing and time!
Mage targets are not case-sensitive, but in our docs we use camel case to make it easier to read. Run the following commands from the porter directory to install mage:
$ go run mage.go EnsureMage
$ mage
This is a magefile, and is a "makefile for go". See https://magefile.org/
Targets:
<List of available targets>
You know that your $GOPATH/bin is configured correctly if you see a list of mage targets.
You can enable tab completion for mage as well, so that you can type
mage t[TAB]
and it will complete it with the name of matching targets.
-
Install bash-completion if it isn’t already installed with either
brew install bash-completion
(macOS) orapt install bash-completion
(debian/ubuntu) depending on your operating system. -
Copy the mage-completion.sh script to a local directory:
cp scripts/mage-completion.sh ~
-
Open your ~/.bash_profile or ~/.bashrc file and add the following line to the file:
source ~/mage-completion.sh
-
Now load the changes to your bash profile with
source ~/.bash_profile
orsource ~/.bashrc
.
Configure Signing
Porter requires that all commits are signed. Run the following command to tell git to automatically sign your commits in the Porter repository:
make setup-dco
Build Porter
Now that we have the source code, let’s build porter and make sure everything is working.
make build
You may see a message about your Go bin not being in your PATH. If that happens,
Add $GOPATH/bin to your PATH and then run
make build
again. It should work now but if it doesn’t, please let us know!
Verify Porter
After you have built Porter, the resulting porter
command-line tool is placed
in the bin directory. Let’s try running porter to make sure everything worked:
./bin/porter help
Now you that you have built Porter, let’s try running a bundle to make sure Docker is installed and configured properly too. This is an abbreviated version of our QuickStart. If you are new to Porter, we recommend trying the QuickStart as well to learn how Porter works.
Use the locally built porter
First let’s do some quick configuration so that you can use the porter executable that you just built, instead of the installed porter. This change isn’t permanent and only affects your current shell session.
If you skip this set up, and ./bin/porter
without PORTER_HOME set it
will use the files in the ~/.porter
instead of what you built. This can
result in not actually using your local binaries in bin.
Bash
export PORTER_HOME=`pwd`/bin
alias porter=`pwd`/bin/porter
PowerShell
$env:PORTER_HOME="$pwd\bin"
Set-Alias -Name porter -Value "$pwd\bin\porter.exe"
Let’s use what you just built and verify that everything is working:
- Make a temporary directory for your bundle:
mkdir -p /tmp/hello-world cd /tmp/hello-world
- Create a new bundle:
porter create
- Build the bundle:
porter build
- Install the bundle:
porter install
- View your bundle’s status by listing all installed bundles:
porter list
Change Porter
Let’s make a change to Porter by adding a new command, porter hello --name YOURNAME
that prints Hello YOURNAME!
.
-
Create a new file at pkg/porter/hello.go and paste the following content.
This is the implementation of our new command. We are adding a new function to the
Porter
struct calledHello
that accepts aHelloOptions
struct containing the flags and arguments from the command. Our options struct has validation logic so that we can enforce rules such as--name is required
.package porter import ( "fmt" "github.com/pkg/errors" ) // Define flags and arguments for `porter hello`. type HelloOptions struct { Name string } // Validate the options passed to `porter hello`. func (o HelloOptions) Validate() error { if o.Name == "" { return errors.New("--name is required") } return nil } // Hello contains the implementation for `porter hello`. func (p *Porter) Hello(opts HelloOptions) error { fmt.Printf("Hello %s!\n", opts.Name) return nil }
-
Create a new file at cmd/porter/hello.go and open it in your editor.
This is the definition of the command. It includes the name of the command, its help text displayed by
porter help
, defines the flags for the command, and says how to validate and run the command.package main import ( "get.porter.sh/porter/pkg/porter" "github.com/spf13/cobra" ) func buildHelloCommand(p *porter.Porter) *cobra.Command { // Store arguments and flags specified by the user opts := porter.HelloOptions{} // Define the `porter hello` command cmd := &cobra.Command{ Use: "hello", Short: "Say hello", PreRunE: func(cmd *cobra.Command, args []string) error { return opts.Validate() }, RunE: func(cmd *cobra.Command, args []string) error { return p.Hello(opts) }, } // Define the --name flag. Allow using -n too. cmd.Flags().StringVarP(&opts.Name, "name", "n", "", "Your name") return cmd }
-
Open cmd/porter/main.go and look for the line
cmd.AddCommand(buildVersionCommand(p))
. Add your new hello command to the supported commands by adding the following line before we add the version command:cmd.AddCommand(buildHelloCommand(p))
Test Your Changes
After you have modified Porter, and aliased the porter
command to use your
local changes, let’s rebuild and test your changes.
- Build porter to incorporate your new command by running
make build
. - Run
porter hello --help
to see the helptext for your command. - Run
porter hello --name YOURNAME
to try out your new command.
$ porter hello --name Carolyn
Hello Carolyn!
That verifies your change but let’s also run the unit tests and end-to-end tests to make sure there aren’t any regressions.
make test-unit
mage testSmoke
Celebrate!
You can now build Porter, modify its code and try out your changes! 🎉 Your next steps should be to read our Contributing Guide to understand how to find an issue and contribute to Porter.