TL:DR; - https://rnelson0.com/2015/04/15/impro...
There are two tools that fit the bill: librarian-puppet and r10k.
librarian-puppet works by using a Puppetfile (simialr to a Gemfile in ruby), which lists versions of modules and where to get them (Puppet forge, git repos) This tool DOES handle recursive dependency resolution, but it is only designed to pull down a single set of modules.
Here are the official puppet docs on using r10k (it comes shipped with PE, but it works with regular puppet as well). r10k works with the same Puppetfile format as librarian-puppet, except you have to define every dependency, as it doesn't do dependency resolution. However, it is designed to reference what is known as a control-version repository, which it pulls down from git, gets the Puppetfile from it for every branch, and creates an environment based on the branch names.
Here are a few posts explaining r10k, and the workflows required.
an ask.puppet.com response
rnelson has may great articles on the topic..
You can create a webhook on your puppet master, and a trigger in your git repository that every time the control repository is updated, it will rebuild the environments on your master based on the updated control-version repo. The workflow would then be similar to a 'jenkins only' merge to master/trunk. You could work on your modules, (implement rspec-puppet tests, beaker tests for integrations, and possibly tests with your software on the instance to make sure it still works), check them in on a feature branch. When that feature branch passes muster, jenkins would then merge your checkin to trunk