Our documentation lives in scattered Readme's
At FinanzRitter, we have a lot of repositories. Some do small things, some do big things. All are documented in Readme files, written in Markdown. That works really well for us, because it allows us to keep the documentation where the code lives. However, when we're onboarding new developers we struggle to get them up to speed because:
- all the information is scattered and we don't have much documentation that would explain where everything is and how it connects to work together.
- We're also missing pieces on why we structured things a certain way.
- And lastly, we have no place for things that don't really belong anywhere: code snippets, how-tos etc.
We need a system to combine all Readme's and a little more
With this struggle, we set out to find a solution.
- We don't want to separate our documentation from our code base.
- We don't want to maintain the same documentation in two different places.
- We don't want to put a lot of extra work into it.
- We do want the ability to add documentation that does not belong to a specific repository but rather explains overarching concepts.
- We do want to have one tool to rule them all. One place where everything comes together.
Why we chose Outline
I'm not affiliated in any way with Outline.
Outline is, as they put it: „Your team’s knowledge base“. It's an online documentation tool built on the Markdown syntax. We chose it over competitors like Confluence, Notion and Slite because Outline is:
- simple, easy to use and minimalistic in design. It does one thing and does it well.
- open source (not strictly FOSS), built in public and can be self-hosted.
- has a solid API.
- has a fair price structure starting at 10 USD / month for 10 users (not per user)
Using the API to push Readme's to Outline
Outline supports collections (we have, for example a collection for frontend documentation, for backend documentation, infrastructure and onboarding). Collections contain pages and subpages. So you have plenty of options to organize things.
We integrated our Readme's into Outline using their API and a job in our GitLab pipelines. The pipeline will take the contents of the Readme and push them to the Outline API, overwriting a given page completely. With a little more work it should also be possible to only overwrite a certain section of a page, but we opted for completely overwriting them for ease-of-use and safety.
So for example, our 'Frontend' collection contains a page called 'Data Management', explaining the overall concept of handling data on the client. It has three sub-pages, one for every repository we have that plays a role in managing the data. Those are updated automatically to mirror the Readme's on GitLab:
Frontend (Collection)
→ Data Management (Page, written in the Outline editor)
→ DataCollection (Subpage, synchronized from the repo's Readme on GitLab)
→ DataStore (Subpage, synchronized from the repo's Readme on GitLab)
→ DataVersion (Subpage, synchronized from the repo's Readme on GitLab)
How it works
- We add a new
document
stage to our GitLab CI pipeline, as well as adocument
job. This should run after a release job. - This job reads the
README.md
from your repository. And replaces the headline (which is expected to be# Your Repository
) with a warning: „This page automatically mirrors the documentation of *Your Repository. Don’t make changes here or they will be lost with the next release.”* This marks the page as being automatically mirrored to Outline and prevents us from loosing manual changes. We don't have a two-way synchronization here! - Lastly, we sent the modified Markdown inside a JSON object to the Outline API.
Setting up the integration
1. Create a new page in Outline
Create a new page / subpage in Outline and collect its alias from the URL. For example, an alias might look like this:
how-to-set-up-outline-integration-nkOq0DuntG
2. Get an API Token
Create an API token in the settings: https://your_organization.getoutline.com/settings/tokens
and save it to your GitLab repository or group as a new CI environment variable. We're naming it OUTLINE_AUTH_TOKEN
. It will now be available to your job. You should also make it protected. If you do so, make sure the job is only running on protected branch- or tag-pipelines or it will fail because of the missing API token.
3. Add the job to your pipeline
Add the following job at the end of your .gitlab-ci.yml
:
document:
stage: document
before_script:
# depending on your base image, you probably need to install jq
- apt update && apt install -y jq
script: |
ORIGINAL=$(cat README.md)
WARNING=':::info
This page automatically mirrors the documentation of [Your Repository](https://gitlab.com/your_username/your_repository). Don’t make changes here or they **will be lost** with the next release.
:::
\'
DOCUMENT=$(echo "${ORIGINAL/\# Your Repository/$WARNING}" | jq -Rs .)
curl https://your_username.getoutline.com/api/documents.update \
-X POST \
-H "authorization: Bearer $OUTLINE_AUTH_TOKEN" \
-H 'content-type: application/json' \
-H 'accept: application/json' \
--data-raw "{\"id\": \"your_page_alias\", \"text\": ${DOCUMENT}}"
Make sure to adapt the job to your repository. You need to replace:
- The name (
Your Repository
) and URL of your GitLab repository (https://gitlab.com/your_username/your_repository
) - The sub-domain or URL of your Outline instance (
https://your_username.getoutline.com
) - The page alias (
your_page_alias
) - Possibly the name of your Readme. We use
README.md
.
Also, the rules or only/except (depending on what you are using) should match the configuration of your release job(s). So the documentation is only published when a release has been built.
You also might want to add allow_failure: false
to the release job(s), if they have a manual trigger. Otherwise, the document stage will run regardless of their success.
Caveats
- Outline has a feature that allows 'Collaborative editing' in real-time with multiple people. It is disabled by default (February 2022) and can be enabled in the Features section of the settings. If active, the integration does not work. I haven't digged deeper into this. Maybe someone has a solution?
- Using the script above, you can only publish one Markdown file. If you have multiple, you need to add multiple jobs or adapt the script accordingly.
- This is currently GitLab-only. It sure would be nice to have A GitHub Action for other folks. If someone creates one, please let me know!
Feedback for Outline
- Your API documentation is good, thank you! Maybe it can be improved regarding the collaborative editing?
- Your WYSIWYG editor often does not behave as I'm used to, especially in regards to lists. It's not buggy, but it could use some fine-tuning.
- It would be amazing to have an integration like this natively!
- Your design is really clean, I enjoy using Outline very much!
Top comments (4)
Interesting article, but one clarification: Outline uses a weird license, the Business Source License, that definitely is NOT an open source license.
Then I should clarify a bit more – "open source" only means that you can read the source code here, it does not imply that it is also Free Open Source Software, licensed under a permissive license acknowledged by the Open Source Initiative. Their license seems to be based on the Apache License with only one exception: it forbids use as a competitor. While slight deviations of Open Source Licenses are not a great idea in general, I find it reasonable that they want to protect their revenue. Maybe dual licensing would have been the better option.
Anyhow, thanks for the heads-up, I'll add a hint to the article.
Viewable source doesn't make something open source. Software doesn't have to be gratis (free as in beer) to be open source, but it does have to be libre (free as in freedom). If a license restricts libre, as the BS License does, then it isn't open source.
I understand what you mean and I‘m not saying you’re incorrect. I just feel like the Business Source License deserves a little better. It is not technically an open source license, but it ticks many boxes and even Bruce Perens does not dismiss it, apparently. So it really is in kind of a gray area. Who knows, maybe it will be an acknowledged license in the future. For me, it is close enough to call it open source with an asterisk.