License
Copyright (C) 2015-2024 The Open Library Foundation
All software is distributed under the terms of the Apache License, Version 2.0.
See the top of each repository’s README document and its LICENSE file. See the Contributor License Agreement section below.
Issue tracker
The FOLIO Issue Tracker is at issues.folio.org and see the usage guidelines which encourage reports of various types of issue and explain how to.
Git and branches
For all FOLIO code repositories, we are trying to follow GitHub Flow.
In short, the master branch is always the head of latest development. Anything merged into master should be of such good quality that at any time a snapshot from master passes all tests, and can be deployed. That is not to say that it will be free of bugs; we are not superhuman.
All work is done in feature branches, and merged to master via pull requests.
Fork GitHub repository
Most people have write permission to only the few https://github.com/folio-org repositories that they regularly work on via their GitHub Teams. For contributions to any other repository you will need to “fork” it to your personal GitHub account, see the GitHub notes about working with forks.
The starting point is the cloned folio-org repository as origin
on your local
machine and looks like this:
git remote -v
origin https://github.com/folio-org/folio-ansible (fetch)
origin https://github.com/folio-org/folio-ansible (push)
These are the steps to convert it to a triangular workflow:
git remote rename origin upstream
git remote add origin https://github.com/YOUR_USERNAME/folio-ansible
git config remote.pushdefault origin
git config push.default current
This is the result:
git remote -v
origin https://github.com/YOUR_USERNAME/folio-ansible (fetch)
origin https://github.com/YOUR_USERNAME/folio-ansible (push)
upstream https://github.com/folio-org/folio-ansible (fetch)
upstream https://github.com/folio-org/folio-ansible (push)
Now fetch
and pull
will use the folio-org upstream
repository. A push
will use your personal origin
fork from where you can create the pull request.
A maintainer will merge it to the upstream
repository.
Commit messages
Try to follow the commit message guidelines in https://chris.beams.io/posts/git-commit/. The key points are:
- Separate subject from body with a blank line
- Limit the subject line to 50 characters
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how
The last point is the most critical:
A diff will tell you what changed, but only the commit message can properly tell you why.
Also consider mentioning relevant Issue identifiers (e.g. OKAPI-258). This assists people to follow the reasons, and enables the issue tracker to automatically link to the related commits.
Retain git commit history
Make every effort to not lose the history. The commit history is important for various reasons, not only that it shows the development track of resources, but also that it contains the attribution required by the Apache License.
For example when there is a need to rename a file, be sure to use ‘git mv …’ to move the file content and its history, rather than simple filesystem rename or copy-and-remove commands.
Feature branches
Feature branches should be branched off from the master. The naming of those is not strict, but if you start a branch to fix issue okapi-xxx filed in issues.folio.org then you might well call the branch okapi-xxx (or if you want to be more descriptive, something like okapi-xxx-contribution-guidelines):
git checkout -b okapi-xxx
You can commit stuff as you go, but try not to push obviously broken stuff into GitHub, not even in your development branch – that will be visible for the whole world, and there is automatic CI testing for each branch so pushing a broken commit will cause some emails. But if you need to share the code, for example for collaborating, of course you need to push it. Naturally you will write decent commit messages explaining what you have done.
The first time you want to push your branch to GitHub, you may encounter an error “The current branch okapi-xxx has no upstream branch”. Git tells you what to do, namely:
git push --set-upstream origin okapi-xxx
Once you have done that once, a simple git push
will be sufficient
thereafter.
While developing your own branch, pull in the master every now and then, and resolve any conflicts that may be there. If you don’t, your branch will diverge further from master, and the final merge of your work back into master will be difficult to resolve.
When you are all done, pull master in again, to make sure your branch merges cleanly and passes all tests. Commit the merge, and push to your branch:
git push
You may also want to git rebase
your branch, compressing multiple commits
into one, and editing the commit messages.
git bisect finding a merge commit
git bisect
helps to find a commit that has introduced a bug
(git bisect manual).
When bisecting, the commits within a feature branch of a pull request should be
skipped because it is unknown whether they build successfully. Ori from Smartly published
“Git Bisect Debugging with Feature Branches”
with this code snippet that skips those feature branch commits:
git bisect start master 75369f4a4c026772242368d870872562a3b693cb
for rev in $(git rev-list 75369f4a4c026772242368d870872562a3b693cb..master --merges --first-parent); do
git rev-list $rev^2 --not $rev^
done | xargs git bisect skip
Requesting a merge
Go to the relevant repository’s GitHub page at the “Branches” tab. It shows some recently pushed branches – your one should be there too. Ensure that the initial branch does build in the CI before proceeding.
Consider the items in the relevant Pull requests checklists before proceeding, and refer to the Development, design, and review processes.
When ready, next to your branch is a button “New pull request”. Select that. Provide a clear title for the PR, including a Jira ticket number, as explained in those checklists.
If you are using a fork, then the process is a little different. Start from your fork and select “New pull request”, then select your head fork and branch.
It should show that it is able to merge, so select the “Create pull request” button under the comment box.
If your pull request is instead to seek feedback, then say in the description and title that it is not yet ready to merge. Describe the items for which you want assistance.
After the pull request is created, add yourself as the first assignee. Some repositories have explicit “code owners” which will be automatically invited to review. If needed, then invite other specific reviewers.
Merging pull requests
When someone has assigned a pull request to you or requested your review, check out the branch, and look at the git log, and the code, and decide whether all is good. You can also look at the commit messages and code changes in GitHub.
Consider the items in the relevant Pull requests checklists.
If there are small details, you can fix them yourself, commit and push to the
branch. Do not copy-and-paste the content as this loses the commit history that
contains the attribution required by the Apache License and is used for the
merge check
of the git branch
command; if needed create a new branch from pull request’s branch.
If there are serious issues, you can close the pull request without merging, with a comment explaining why you could not do it.
Once all is well, you can use GitHub’s interface. Just go to the conversation tab, and select “Merge Pull Request” (don’t squash, don’t rebase, learn why). Edit the comment if necessary, and select “Confirm Merge”. GitHub should tell you that the “Pull request successfully merged and closed”. Next to it is a button to “Delete the Branch”. For a simple feature branch, you might as well delete it now, it has served its purpose. But if you think there is more work that should be done in this branch, of course you don’t delete it.
This merging of the pull request’s branch okapi-xxx can also be done on the command line, if you prefer.
git checkout master
git merge okapi-xxx
When done, you probably want to delete the local branch from your own machine
git branch -d okapi-xxx
Contributor License Agreement
The FOLIO Project uses the Apache License, Version 2.0 for its code and requires developers to acknowledge their contributions to the project using this license. The contents of the Contributor License Agreement (CLA) are stored in a Gist on GitHub:
See accepting the contributor license agreement for more details.
Automation
The FOLIO build, test, and deployment infrastructure is described separately.
Releasing
Refer to the specific Release procedures.
Later, if there are bugs in the released version, work can continue on the version branch, and we can release a new minor version from the branch. Some changes may be cherry-picked from the master, or from the version branch to the master, as need be.
Version numbers
Since (almost) all components have hard separation between interface and implementation, we need to keep two kinds of version numbers, one for the API, and one for the implementation code. To make matters worse, any FOLIO module may implement several interfaces. The Okapi Guide further describes its handling of Versioning and Dependencies.
API/interface versions
The API versions are two-part major.minor numbers, such as 3.14
The rules are simple:
- If you only add things to the interface – e.g. a new resource or method on existing resources – then you increment the minor number, because the API is backwards compatible.
- If you remove or change anything, you must increment the major number, because now your API is no longer backwards compatible.
For example, you can add a new resource to 3.14
, and call it 3.15
.
Any module that requires 3.14
can also use 3.15
. But if you remove
anything from the API, or change the meaning of a parameter, you need to
bump the API version to 4.1
Implementation versions
We follow the rules commonly known as semantic versioning to version both FOLIO modules (aka apps) and any other FOLIO software components (e.g. utility libraries of frameworks), so-called non-modules.
The implementation versions are three-part part numbers: major.minor.bugfix, such as 2.7.18
.
FOLIO modules may implement more than one interface so they are versioned independently from any particular interface, they need to however follow the same rules:
-
For modules, the major part should be incremented if you implemented a backwards incompatible change to the API(s), (this will be indicated by the major number change in the particular API). For non-modules this may also mean any major changes with respect to functionality or implementation that don’t necessarily result in interface changes, e.g. migration to a new DB backend.
-
For modules the middle part should be incremented if you implemented an addition to the API(s), (the minor version of the particular API has been changed). For non-modules it may also mean any additional functionality. For both, the change must be backwards compatible with respect to any client code or agents.
-
For modules the bugfix part should be incremented if you haven’t changed anything in the API or added any new functionality but only fixed implementation bugs, etc. The same applies for non-modules.
Module implements one interface
In the simplest case, a module implements just one interface, but since we want to be able to register any functional changes to the module by
increasing the module’s minor version number, we will keep two independent versions for the API and implementation. For example, a module with version 2.71.0
may implement the checkout API at 3.14
. When the checkout API changes to 3.15
, and the module implements the change,
the module version becomes 2.72.0
. In the case where only the implementation is corrected (bugfixes with no functionality changes)
and the module still implements the checkout API at 3.14
, then the module version gets bumped to 2.71.1
.
Module implements multiple interfaces
A module can implement more than one interface, and more than one major version of any of them. In that case the version numbering is necessarily more complex. Again, there does not have to be any correlation between the module version and the version of the interfaces it implements.
For example, if the circulation module version 2.71.0
can implement the
checkout API version 3.14
and the checkin API version 1.41
then the
rules are still the same:
- If the change doesn’t follow any change to any API and is merely a bugfix, increment the last part to
2.71.1
- If you add new features that e.g. follow the extended APIs, increment the middle part to
2.72.0
- If you implement any backwards-incompatible change to any API, or drop any
API at all, increment the module version to
3.0.0
The most common case is probably when we need to add a new, incompatible API
to a module, but want to keep the old one too. In such cases we only increment
the module version to 2.72.0
but mark that it provides the API versions
3.14
and 1.41
Trailing zero for module/non-module versions
Changes to major and minor version follow from adding new features or larger code refactoring, usually planned in advance. The bugfix version number is reserved for tracking changes caused by malfunction that may be hard to predict.
As such, every new version for a particular major.minor series (e.g. 2.71
) start with bugfix
version set as 0, effectively 2.71.0
. This indicates that no bugs have been discovered (yet)
and no hotfix releases provided.
Naming conventions
There are guidelines for Naming conventions of modules, permissions, endpoints, etc.
Coding style
Style conventions
Follow the coding style that is being used by each repository for each file type.
For JSON key names we use camelCase.
For Java code we basically try to adhere to Sun Java coding conventions (that document is old and unmaintained, but seems to be good enough as it is).
For JavaScript code we follow ESLint, with some exceptions.
Code analysis and linting
All code repositories have linter and code-style analysis facilities implemented as part of their continuous integration build process. The process is explained, along with usage notes and configuration for running those tools locally.
Consistent whitespace
- We indent with two spaces only, because vert.x uses deeply nested callbacks.
- We do not use tab characters for indents, only spaces.
For XML and JSON and RAML files, the same: two-space indent and no tabs.
Some projects do provide a .editorconfig
file.
Remember to set your IDE and editors to remove trailing spaces on saving files,
since those produce unnecessary diffs in Git.
Refer to coding style configuration assistance.
No license header
We do not use a license header in the top of each source code file. Refer to the README and LICENSE file at the top of each repository. See further license information. See configuration assistance for IDEs.
Tests
We aim to write a lot of tests – each module should have at least some kind of test associated with it. These can be traditional unit tests, black-box tests that talk through the WSAPI, and/or proper integration tests.
When hunting down problems, it is considered good form to write a test that demonstrates the problem first, then a fix that makes the test pass.
We have a Jenkins test system that gets invoked when you push something
to a branch, and/or make a pull request. It should flag any errors, but be
nice and run a mvn install
on your own machine before every
git commit
RAML and Schema
Ensure thorough description of the API RAML files and describe schema properties.
Ensure that breaking changes in the API are reflected with a new interface version.
For Okapi, we keep the API specs in RAML files under okapi-core/src/main/raml/
For server-side modules, the raml repository is the master location for the shared traits and resource types, while each module is the master for its own schemas, examples, and actual RAML files.
Reference API documentation is automatically generated by the continuous integration, for modules that are managed with the FOLIO CI.