Thanks for contributing to Kobiton Docs! This guide covers how content for docs.kobiton.com is created, organized, and published. Everyone here is expected to adhere to the Code of Conduct so be sure to review before contributing.
If you're new to docs-as-code, start with First time contributors, otherwise choose a topic below:
- First time contributors
- Build the docs locally
- Antora logs
- Antora playbooks and UI bundles
- Directory structure
- Site navigation
- Site URLs
- Hide a page from search
- Partials
- Directory and file names
- Page types and templates
- Style and voice
- Docker commands
Our docs are managed as docs-as-code, meaning each document is a plain text file stored in a version-controlled repository. Learn more about each topic below:
Version control allows our team to work on documents simultaneously without accidentally overwriting each other's work.
Our document files are written in a plain text markup language and only contain written content--not design content--allowing our team to separate writing-related tasks from design-related tasks.
- Learn about the AsciiDoc markup language
- Get started with the AsciiDoc markup language
- Directory and file names
- Directory structure
The static site generator converts all plain text files to HTML and bundles them with our CSS when you build the docs locally.
When a commit is merged to our main branch in GitHub, the site is automatically published using Docker and GitHub actions.
- Learn about Docker
- Get started with Docker
- Docker commands
- Learn about GitHub actions
- Get started with GitHub actions
We use Antora to generate our static site. Before you can use Antora to generate the site locally, you'll need Node.js and Yarn. Run the following commands to see if they're installed:
node --version
yarn --version
If Node.js and Yarn are installed, install project dependencies by running:
yarn install
To build the docs with logs, run:
yarn local
To view the logs in your web browser, run:
yarn server-docs
Beautified logs are generated in the logs directory when you run yarn local. If AsciiDoc preview is enabled in your text editor, you can select links in the Table of contents or the File column to open that section or file.
Run yarn build to generate standard JSON logs instead.
{"level":"error","time":1691005714646,"name":"asciidoctor","file":{"path":"/Users/<your-username>/kobiton/docs/docs/modules/automation-testing/pages/index.adoc"},"source":{"url":"https://github.com/kobiton/documentation.git","local":"/Users/<your-username>/kobiton/docs/.git","worktree":"/Users/<your-username>/kobiton/docs","refname":"update-contributing-doc","reftype":"branch","startPath":"docs"},"msg":"target of image not found: $NEW-IMAGE$"}
{"level":"error","time":1691005714663,"name":"asciidoctor","file":{"path":"/Users/<your-username>/kobiton/docs/docs/modules/apps/pages/index.adoc"},"source":{"url":"https://github.com/kobiton/documentation.git","local":"/Users/<your-username>/kobiton/docs/.git","worktree":"/Users/<your-username>/kobiton/docs","refname":"update-contributing-doc","reftype":"branch","startPath":"docs"},"msg":"target of image not found: $NEW-IMAGE$"}
{"level":"error","time":1691005714679,"name":"asciidoctor","file":{"path":"/Users/<your-username>/kobiton/docs/docs/modules/devices/pages/about-device-passcodes.adoc"},"source":{"url":"https://github.com/kobiton/documentation.git","local":"/Users/<your-username>/kobiton/docs/.git","worktree":"/Users/<your-username>/kobiton/docs","refname":"update-contributing-doc","reftype":"branch","startPath":"docs"},"msg":"target of image not found: $NEW-IMAGE$"}
{"level":"error","time":1691005714694,"name":"asciidoctor","file":{"path":"/Users/<your-username>/kobiton/docs/docs/modules/devices/pages/index.adoc"},"source":{"url":"https://github.com/kobiton/documentation.git","local":"/Users/<your-username>/kobiton/docs/.git","worktree":"/Users/<your-username>/kobiton/docs","refname":"update-contributing-doc","reftype":"branch","startPath":"docs"},"msg":"target of image not found: $NEW-IMAGE$"}
{"level":"error","time":1691005714712,"name":"asciidoctor","file":{"path":"/Users/<your-username>/kobiton/docs/docs/modules/devices/pages/search-for-a-device.adoc"},"source":{"url":"https://github.com/kobiton/documentation.git","local":"/Users/<your-username>/kobiton/docs/.git","worktree":"/Users/<your-username>/kobiton/docs","refname":"update-contributing-doc","reftype":"branch","startPath":"docs"},"msg":"target of image not found: $NEW-IMAGE$"}
{"level":"error","time":1691005714718,"name":"asciidoctor","file":{"path":"/Users/<your-username>/kobiton/docs/docs/modules/integrations/pages/index.adoc"},"source":{"url":"https://github.com/kobiton/documentation.git","local":"/Users/<your-username>/kobiton/docs/.git","worktree":"/Users/<your-username>/kobiton/docs","refname":"update-contributing-doc","reftype":"branch","startPath":"docs"},"msg":"target of image not found: $NEW-IMAGE$"}Typically, a project uses one antora-playbook.yml and one ui-bundle/ directory to configure Antora. However, our project uses a set of each: one for docs.kobiton.com and one for portal.kobiton.com. In most cases, the playbook will match and the UI bundles will remain site-specific.
The content on docs.kobiton.com is configured in antora-playbook-docs.yml and the ui-bundle-docs directory.
antora-playbook-docs.ymlis used to configure the site name, analytics keys, extensions, UI bundle location, Antora logs, and site URLs.ui-bundle-docscontains all source files for style and design of the site, including the home page tiles.
The help widget on portal.kobiton.com is configured in antora-playbook-widget.yml and the ui-bundle-widget/ directory.
antora-playbook-widget.ymlis used to configure the site name, analytics keys, extensions, and UI bundle location.ui-bundle-widget/contains all source files for style and design of the widget on portal.kobiton.com.
Our project hosts a single modules directory containing an antora.yml file, a ROOT directory, and a directory for each section of content.
PROJECT
└── docs
├── modules
│ ├── ROOT
│ └── example-section
└── antora.yml
Each directory in modules hosts a specific section, including an attachments, images, pages, and partials directory, as well as a nav.adoc file.
modules
└── example-section
├── attachments
├── images
├── pages
├── partials
└── nav.adoc
The pages directory hosts the written content for each section as .adoc files with this file formatting. Content files can be stored directly in pages (such as pages/*) or in multi-nested directories (such as pages/**/*).
example-section
└── pages
├── subsection-a
│ ├── index.adoc
│ └── page-a.adoc
├── subsection-b
│ └── page-b.adoc
├── index.adoc
└── page-c.adoc
At a minimum, each pages directory must contain an index.adoc file, which serves as the section's landing page and is accessible from the navigation bar.
When docs.kobiton.com is generated, these files create the site's navigation bar:
nav.adoc: determines which pages are displayed beneath each section on the navigation bar.antora.yml: determines which sections (or modules) are added to the navigation bar.
Use these files to add sections, pages, and subsections to the navigation bar.
To add a section to the navigation bar, open docs/antora.yml and add the relative path to that section's nav.adoc file beneath the nav key.
name: ROOT
title: Kobiton Docs
version: ~
nav:
- modules/example-section/nav.adocIf the nav.adoc contains at least a landing page, that section will be added to the navigation bar. For example:
nav.adoc input:
.xref:example-section:index.adoc[] // Section landing page
* xref:example-section:page.adoc[]
* Subsection A
** xref:example-section:subsection-a/page-a.adoc[]
* xref:example-section:subsection-b/index.adoc[]
** xref:example-section:subsection-b/page-b.adoc[]nav.adoc output:
A file in the pages directory can either serve as a section's landing page or a content page. At a minimum, a pages directory and nav.adoc file must contain a landing page (as an index.adoc file) so add a landing page before any content pages.
To add a landing page to a section or subsection in the navigation bar, open the nav.adoc for that section.
PROJECT
└── docs
└── modules
└── example-section
├── images
├── pages
├── partials
└── nav.adoc
The landing page must be set to .xref:index.adoc[] at the top of the nav.adoc file, so site URLs are generated properly. Add it to the unordered list. For example:
nav.adoc input:
.xref:example-section:index.adoc[] // Section landing page
* xref:example-section:page.adoc[]
* Subsection A
** xref:example-section:subsection-a/page-a.adoc[]
* xref:example-section:subsection-b/index.adoc[]
** xref:example-section:subsection-b/page-b.adoc[]nav.adoc output:
(Be sure the section's nav.adoc begins with .xref:index.adoc[] so site URLs are generated properly).
To add a content page to a section or subsection in the navigation bar, open the nav.adoc for that section.
PROJECT
└── docs
└── modules
└── example-section
├── images
├── pages
├── partials
└── nav.adoc
Add a cross-reference to the page in the unordered list. For example:
nav.adoc input:
.xref:example-section:index.adoc[]
* xref:example-section:page.adoc[] // Content page
* Subsection A
** xref:example-section:subsection-a/page-a.adoc[]
* xref:example-section:subsection-b/index.adoc[]
** xref:example-section:subsection-b/page-b.adoc[]nav.adoc output:
When you add a subsection, you can choose to add or omit a landing page for that subsection.
To add a subsection with a landing page, first create a directory for that subsection. Add an index.adoc file (which will serve as the landing page) and all related .adoc files:
example-section
├── pages
│ ├── subsection-a
│ │ └── page-a.adoc
│ ├── subsection-b
│ │ ├── index.adoc // Subsection landing page
│ │ └── page-a.adoc // Subsection content page
│ ├── index.adoc
│ └── page-b.adoc
└── nav.adoc
Open the nav.adoc for the main section and add cross-references to the index.adoc to the unordered list and all content files one list-level below it:
nav.adoc input:
.xref:example-section:index.adoc[]
* xref:example-section:page.adoc[]
* Subsection A
** xref:example-section:subsection-a/page-a.adoc[]
* xref:example-section:subsection-b/index.adoc[] // Subsection landing page
** xref:example-section:subsection-b/page-b.adoc[] // Subsection content pagenav.adoc output:
To add a subsection without a landing page, first create a directory for that subsection, then add all related .adoc files:
example-section
├── pages
│ ├── subsection-a
│ │ └── page-a.adoc // Subsection content page
│ ├── subsection-b
│ │ ├── index.adoc
│ │ └── page-a.adoc
│ ├── index.adoc
│ └── page-b.adoc
└── nav.adoc
Open the nav.adoc for the main section and add the subsection title as plaintext to the unordered list, then cross-references for each .adoc file one list-level below it:
nav.adoc input:
.xref:example-section:index.adoc[]
* xref:example-section:page.adoc[]
* Subsection A // Subsection title
** xref:example-section:subsection-a/page-a.adoc[] // Subsection content page
* xref:example-section:subsection-b/index.adoc[]
** xref:example-section:subsection-b/page-b.adoc[]nav.adoc output:
These URL guidelines are strictly enforced to avoid formatting inconsistencies and broken end-user bookmarks.
URLs are generated from three files:
antora-playbook-docs.yml: determines if URLs end in a file extension, like.html.antora.yml: determines if URLs contain subdirectories, likedocs/.nav.adoc: determines if certain URLs containindexstrings.
Use these files to remove file extensions, subdirectories, and index strings from URLs.
The antora-playbook-docs.yml is used to add or remove the .html file extension from URL endings.
- Before:
docs.kobiton.com/devices/install-an-app.html - After:
docs.kobiton.com/devices/install-an-app
For docs.kobiton.com, we do not add file extensions to URL endings, so the html_extension_style key is set to drop in the antora-playbook-docs.yml file:
site:
title: Kobiton Docs
urls:
html_extension_style: drop
content:
sources:
- url: .
start_paths: docs
branches: HEADHowever, if this needs to change in the future, set html_extension_style to indexing or remove the key completely:
site:
title: Kobiton Docs
content:
sources:
- url: .
start_paths: docs
branches: HEADThe antora.yml file in PROJECT/docs/ is used to add or remove a subdirectory from the site URL.
- Before:
docs.kobiton.com/docs/get-started/ - After:
docs.kobiton.com/get-started/
For docs.kobiton.com, we do not add subdirectories, so the name property is set to ROOT in the antora.yml file:
name: ROOT
title: Kobiton Docs
version: ~
nav:
- modules/get-started/nav.adoc
- modules/release-notes/nav.adoc
- modules/integrations/nav.adocHowever, if this needs to change in the future, set name to any hyphen-separated string:
name: kobiton-docs
title: Kobiton Docs
version: ~
nav:
- modules/get-started/nav.adoc
- modules/release-notes/nav.adoc
- modules/integrations/nav.adocThe nav.adoc file in each module is used to generate the site navigation, as well as add or remove index from URLs.
- Before:
docs.kobiton.com/get-started/index - After:
docs.kobiton.com/get-started/
For docs.kobiton.com, we do not add index to URLs, so Antora's list title formatting should be applied to the first index.adoc listed in the nav.adoc file:
.xref:index.adoc[]
* xref:devices:search-for-a-device.adoc[]
* xref:devices:manage-devices.adoc[]
* xref:devices:network-payload-capture/index.adoc[]
** xref:devices:network-payload-capture/configure-the-local-server.adoc[]
** xref:devices:network-payload-capture/configure-the-local-server.adoc[]
** xref:devices:network-payload-capture/configure-an-ios-device.adoc[]By default, all .adoc files in the modules directory are searchable on Kobiton Docs using the Antora Lunr Extension. To hide a page from search results, add the :noindex: attribute to the page's metadata.
= Generate an API token
:navtitle: Generate an API token
:noindex:
Learn how to generate an API token in Kobiton.Antora partials allow you to reuse global and feature-specific content across the docs. Global content (role requirements, pricing, etc.) is located within ROOT/partials, while feature-specific content (supported app filetypes, supported gestures, etc.) is located within that specific feature's partials subdirectory.
ROOT
└── docs
└── modules
├── ROOT
│ └── partials
│ ├── pricing.adoc
│ └── roles-page.adoc
└── apps
└── partials
└── supported-filetypes.adoc
To use a global partial, use the following include statement:
include::ROOT:partial$<filename>.adocTo use a feature-specific partial, use the following include statement:
include::<feature>:partial$<filename>.adocAll directories and files should follow these guidelines:
| Naming Guideline | Before | After |
|---|---|---|
| Only lowercase letters | This Is My TITLE |
this is my title |
| Replace spaces with dashes | this is my title |
this-is-my-title |
| Replace important symbols | i love c++ & c# |
i love cpp and csharp |
| Remove unimportant symbols | this: is my title! |
this is my title |
For example:
automation-testing
└──pages
├── desired-capabilities.adoc
├── download-appium-script.adoc
├── index.adoc
└── supported-client-libraries.adoc
We use the Diátaxis framework to structure our docs. Each Diátaxis category corresponds to one of these templates. Add a template to your .adoc file to get started.
Tutorial docs walk users through their first time attempting a task. Everything a user needs should be available in the tutorial.
For example, a tutorial titled "Your first manual session" would state a learning objective, show the user how to start a session, offer an app for them to install, and walk them through a variety of test steps.
= Title
:navtitle: Title
In this tutorial, we'll walk you through your first...
== Before you start
You'll need to download...
== Step 1
Content.
== Step 2
Content.
. Sub-step
. Sub-stepHow-to docs outline the steps for solving a specific problem. Unlike tutorials, How-tos only focus on a specific problem--not an entire process.
For example, a how-to doc titled "Download an app during a manual test session" would state the goal, give a line of context, and start step one assuming the user has already launched a manual test session.
= Title
:navtitle: Title
Learn how to...
== Step 1
Explain and give steps.
== Step 2
Explain steps.
. Give step
. Give stepReference docs describe a product, not a process. They're for users who know how to complete a process, but need more details about the tools they use to complete a process.
For example, a reference doc titled "Desired capabilities" should list all desired capabilities along with their definition and a brief example. The reference doc shouldn't contain steps for modifying desired capabilities or walk users through their first automation session.
= Title
:navtitle: Title
These are the ... for ...
== Category one
Item.
Definition.
Example.
== Category two
=== Item one
Definition.
Example.
=== Item two
Definition.
Example.Explanation docs explore a topic, which could include its context within culture, its context within Kobiton, how it got here, and where it's headed.
For example, an explanation doc titled "About biometric authentication" should explore a few key milestones in its global development, why It's important to test, and how Kobiton can help.
= Title
:navtitle: Title
<Topic does x...>
== First item
Content.
== Second item
Content.Release notes are not a part of the Diátaxis framework, so we use the following templates for our release notes.
= Kobiton X.X release notes
:navtitle: Kobiton X.X release notes
_DATE_
== First item
Content.
== Second item
Content.= Kobiton X.X release notes (Legacy)
:navtitle: Kobiton X.X release notes
_DATE_
== First item
Content.
== Second item
Content.One day we'll create our own, but for now we use the Microsoft Style Guide to inform our style and voice.
We use Docker and GitHub actions to publish content to docs.kobiton.com and portal.kobiton.com.
To create a docker image for docs.kobiton.com, run:
docker build -t kobiton/docs:1.0 -f docker/docs/Dockerfile .To create a docker image for the help widget on portal.kobiton.com, run:
docker build -t kobiton/widget:1.0 -f docker/widget/Dockerfile .





