Skip to content

Code contributions, PRs, PGP and DCO

Jim Klimov edited this page Aug 6, 2024 · 14 revisions

Pull requests (PRs)

To experimentally research and solve practical issues, people are encouraged to fork NUT, git clone their copy, create a new feature branch name, and build it locally to try their fixes and enhancements, add debug information or step through running code with a debugger. This is not rocket science, everyone on the project started from scratch some time ago, and it is the sum of "someone scratching their itch" increments layered over the decades.

NOTE: PRs to update or clarify documentation are particularly welcome across the board in FOSS ecosystems. They do not require much coding skill, but allow to dive deeper into the project and understand it. Importantly, people well versed in a project tend to consider some things "obvious" or to pile on lots of technical details without an "executive summary" to help readers grasp it, and it does take an enthusiastic new-comer to make the existing documentation more useful and friendly to other new-comers. In case of NUT, there is also an aspect of balancing the information stored in the source code repository (and so eventually available on the NUT web site, in release archives and distribution packages), and that dynamically contributed to the on-line NUT wiki on GitHub.

For one of the ways to build NUT in a workspace to run it from (or eventually install) with configuration pre-sets inherited from the existing OS packaging, see Building NUT for in-place upgrades or non-disruptive tests.

Useful reading in this regard also includes Changing NUT daemon debug verbosity and Information for developers.

Eventually, people are encouraged to share back -- so their fixes get reviewed by others, tidied up (e.g. to address documentation, portability or code style issues, if any), merged and then maintained by the community rather than stored in a personal fork that one has to re-synchronize and re-build to get other updates from the core project. So it is both about making a better maintainable version of this change, about helping others and about offloading the burden from your shoulders :)

While there have been many ways of "sharing back" tried over time, including posting of patch files or "diff" text blocks in e-mail or issue discussions or on personal pages, the modern approach (also with a much faster turn-around on maintainer side) is to post GitHub pull requests. This is where your NUT fork comes in: after completing the development on your side, you git commit ... locally (into a non-master feature branch), and git push the changes to your fork. The GitHub web interface then offers to open a pull request to "contribute" these changes to the parent project, and starts automatic builds and checks on the NUT CI farm and other resources to make sure that the proposed change is viable on the many platforms that NUT aims to run on, that there are no spelling errors and other discrepancies.

Some of these checks involve ensuring that you are the author of the change set, and that you agree to having it contributed -- see the sections on PGP and DCO below. While they are recommendations and not strict requirements at the moment, they make life easier for the project in the long run.

While for typical contributions you would fork the project on GitHub, edit your fork (git clone to a local workspace, edit, git add the changes, git push back to your fork), and GitHub Web UI would offer to "Contribute" a pull request from your recently changed code, for a small change like documentation improvements (and if you do not intend to actively develop the particular project later on), it can all be done in GitHub Web UI: track down the file with sources to edit, click the pencil on top, then it would offer to create a fork if you don't have one, and open a simple web-editor. Eventually you would "Commit" the changes to save them to an auto-made branch like patch-1 and it would go to post a PR from that. So just a few clicks without leaving the browser.

Developer Certificate of Origin (DCO)

Please note that to err on the safe side, the NUT project like many others asks code contributors to "sign off" their Git commits. This is a common approach used in the FOSS industry over the past couple of decades, primarily for contributors to declare that they have the right to publish the work and that they agree to the terms of a project's license (mostly GPLv2+ for NUT, see https://github.com/networkupstools/nut/blob/master/COPYING for specific details). You can see the exact wording of the DCO at https://github.com/networkupstools/nut/blob/master/LICENSE-DCO or https://developercertificate.org/

After issue #1994 (see more details there) and PR #1995 (to update the docs), the NUT GitHub organization has a "DCO" application attached among pull request checks. It verifies that all of the commit messages for the proposed code increment include one or more Signed-off-by: User Name <[email protected]> patterned lines.

On the technical side it means that whenever you commit code to your NUT fork, you have to either type (or copy-paste) the line -- notably in the GitHub web-editor, tick the option in an IDE with a smart Git UI, or use the -s option for the git command line tool, e.g. git commit -sm 'DIR/FILENAME: Adding a new feature to...' and it would use your configured user.name and user.email for this local repository (or OS account or system-wide defaults -- depending on your workstation settings).

The git tooling intentionally lacks a configuration option to automate the sign-off, as it is a sort of legally meaningful operation that should be consciously done by humans regarding the change sets they have personally made or relayed. That said, git repository hook scripts are possible, like the one proposed at https://stackoverflow.com/a/46536244/4715872 (for git newer than 2.15), and may help if someone contributes to the same project frequently. As a human-made addition to their local repository, it probably has a similar effect of being "consciously done" (though I am not a lawyer to warrant that).

PGP

The "Pretty Good Privacy" (PGP) technology, and particularly several generations of its "GNU Privacy Guard" (GPG or GnuPG) implementation, have been long used to provide people with cryptographic keys usable to encrypt their messages and/or sign them (and ensure verifiable non-modification). Unlike the centralized certificate authority approach, PGP fundamentally relies on direct exchange of keys between parties (I meet you at a conference and tell you my public key, and so you know that it is mine); however in practice this is often aided by publication of maintainer key information along with the projects, developer keys on their web pages, or by use of numerous sites and services for key exchange. Many operating systems also include packages with key chains as the automated basic level of trust to developers/maintainers involved in the production of application packages for those systems.

In case of modern development, this can be integrated with Git to additionally certify that it was "you" that posted a particular commit or produced an "annotated tag" (such as used for release snapshots), and not just a random someone who had configured your name and e-mail address into their git setup.

While it is possible to have one key for everything, it is generally recommended to have a "master" key (ideally stored on an offline medium) which is only used to issue or edit sub-keys, their roles, your identity information, etc. Those sub-keys in particular can be made separately for each workstation you have access to, so in case of a breach you can just revoke the relevant sub-key, republish your chain, and the untrustworthy credential can not be used by a malicious party to impersonate you. They can also be configured with an expiration date, so unless you edit the key to refresh that attribute, it will eventually become "useless" automatically.

GitHub allows to upload the PGP public key information into a developer's profile, so that commits signed by that key with a matching identity (e.g. e-mail address) would show up as "Verified". Note that to update a key (e.g. after editing identities or changing the set of sub-keys) you should delete the older instance in the GitHub profile and upload the new one after that -- "updates" are not supported. Note that "Verified" commits made earlier using an identity (e-mail) which you remove later would become "Unverified" in the GitHub interface. Allegedly, there are also ways to use the PGP key for git protocol authentication so you do not need a separate SSH key, if you are lucky.

In the git tooling, you can use the -S command-line option to create a PGP-signed commit or tag. Unlike the "Signed-off-by" feature, you can also configure the repository/account/system options to create signed commits, and which key to use where (e.g. to handle FOSS and work repositories separately). You would generally need to specify your key password for each commit, although it is possible to set up credential caching so you are not asked for that every time past the first after boot/login. Beside POSIX platforms, this works fairly well on Windows with Git Bash and WSL2 as well.

You are advised to read up on the set-up from various sources, to get a better grasp of it. Some pages that helped in my own set-up included:

Some hints about GPG+Git troubleshooting:

  • To check that signing a commit worked, you can verify it with:
:; git log --show-signature -1
  • Make sure your git user.signingkey configuration is set up (global, or repo-specific) and exists in known GPG keychain, and was not revoked/expired:
:; gpg -K --keyid-format LONG \
   | grep -w "`git config --get-all user.signingkey`"
  • Also make sure your commit username and email match the ones used in PGP identity:
:; gpg -K --keyid-format LONG \
   | grep "`git config --get-all user.name`" \
   | grep "<`git config --get-all user.email`>"
  • To auto-sign every commit (needs other setup, e.g. the user key, possibly path to gpg via git config --global gpg.program gpg, etc. -- as detailed in articles):
:; git config --global commit.gpgsign true
  • It sometimes helps to un-stuck the agent: gpgconf --kill gpg-agent ; sleep 3; gpgconf --launch gpg-agent
  • GPG agent may be picky about the terminal you use when it wants you to enter the key passphrase, like a session under su or screen into a user account. To troubleshoot commit-signing failures, you can ask it directly, e.g.:
:; GIT_TRACE=1 git commit -Ssm 'Test signed commit'
22:40:43.918331 git.c:444               trace: built-in: git commit -Ssm 'Test signed commit'
22:40:43.919303 run-command.c:664       trace: run_command: gpg --status-fd=2 -bsau FF1234DEAF5678F00D
error: gpg failed to sign the data
fatal: failed to write commit object

:; echo "dummy" | gpg -bsau "`git config --get-all user.signingkey`"
gpg: signing failed: Inappropriate ioctl for device
  • For this issue in particular, it may help to set up your user ~/.profile to include:
GPG_TTY=$(tty)
export GPG_TTY
  • For some further SSH integrations read up on these tricks for your .profile:
SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
export SSH_AUTH_SOCK

Troubleshooting

Failed DCO checks due to lack of Signed-off-by lines in PRs

A maintainer can click into the failure details and there should be a button to "Set DCO to pass". This should not be encouraged, but helps with old PRs that are stuck in review for whatever reason and the original author is no longer reachable.

An author can do something like git rebase upstream/master --signoff && git push --force-with-lease (the failed DCO check details would suggest a more specific set of commands), or git commit --amend -s to fix the single latest commit.

For numerous commits with mixed authorship (and probably without "merge" commits), the with BACK, SIGNOFF and COMMIT_USER_FILTER envvar options for https://github.com/jimklimov/git-scripts/blob/master/git-reauthor script can be useful.

Clone this wiki locally