Docs with Efficient Downstreaming in Mind

About the author

243761
  • Free Software geek.

  • Technical Writer @ Red Hat.

  • Community: Eclipse Che.

Previous communities

  • Ansible Molecule (bug triage)

  • Rudder (docs)

  • Jeudisdulibre.be (organizer)

  • Fusioninventory & GLPI (docs)

Jobs names

  • Cloud Architect

  • DevOps

  • SysAdmin

Culture, Automation, Measurement, Sharing.

Context

  • Upstream project

  • Downstream product

Upstream project

che docs website

Downstream product

State of downstreaming

  • Ad-hoc script for previous release

  • Objective: be creative & automate

People

  • Technical writers: 3-

  • Developers: 30+

Development speed

ludicrous speed
Figure 1. Ludicrous speed

The stream metaphor

Upstream?

kayak cascade

Downstream?

Kayak Sailing on the Columbia River

Modular Documentation

Assemblies and modules

  • Assembly

  • Concept module

  • Procedure module

  • Reference module

AsciiDoc capabilities

  • Include statement ⇐⇒ block level variation

  • Attributes (variables) ⇐⇒ inline variation

Tools: newdoc

  • Generate empty modules

$ pip3 install newdoc
$ newdoc --procedure "Setting up things"
File successfully generated.
To include this file from an assembly, use:
include::<path>/proc_setting-up-things.adoc[leveloffset=+1]

Tools: test-adoc.sh

  • Validate structure and links

$ test-adoc.sh src/main/pages/che-7/administration-guide/assembly_securing-che.adoc

Testing file: src/main/pages/che-7/administration-guide/assembly_securing-che.adoc

  Document type: assembly

  [ FAIL ]  The 'securing-{prod-id-short}' ID does not include the 'context' attribute.
  [ FAIL ]  Assembly does not contain any included modules.

Checked 6 item(s) in 1 file(s), found 2 problem(s).

Build & test

Building some guides

tox -e crw-installation-guide,crw-administration-guide -p auto
✔ OK crw-administration-guide in 20.997 seconds
✔ OK crw-installation-guide in 23.842 seconds
________________________ summary _________________________
  crw-installation-guide: commands succeeded
  crw-administration-guide: commands succeeded
  congratulations :)

Downstreaming filters

[testenv:crw-filters]

description =
    Run downstreaming process for CRW (copy, parse, substitute from upstream to downstream).

commands =
    which grep jinja2 sed
    ; execute the filters
    {toxinidir}/tools/downstreaming-crw/downstreaming.sh -f
    ; build and validate the docs
    tox -p auto -e crw-administration-guide,crw-end-user-guide,crw-installation-guide,crw-release_notes

changed
    {toxinidir}

deps =
    jinja2-cli[yaml]

whitelist_externals =
    tox
    which
    {toxinidir}/tools/downstreaming-crw/downstreaming.sh

Running filters

tox -e crw-filters
...

✖ FAIL crw-end-user-guide in 13.299 seconds
✔ OK crw-installation-guide in 17.319 seconds
✔ OK crw-administration-guide in 22.544 seconds
___________________________ summary __________________________________________________________________________
  crw-administration-guide: commands succeeded
ERROR:   crw-end-user-guide: parallel child exit code 1
  crw-installation-guide: commands succeeded
  crw-release_notes: commands succeeded
ERROR: InvocationError for command /home/remote/ffloreth/.local/bin/tox -p auto -e crw-administration-guide,crw-end-user-guide,crw-installation-guide,crw-release_notes (exited with code 1)
___________________________ summary __________________________________________________________________________
ERROR:   crw-filters: commands failed

Get fresh upstream

[testenv:crw-update]

description =
    Update CRW upstream documentation.

commands =
    ; clone the upstream repository if necessary
    bash -c "test -d {toxinidir}/tools/downstreaming-crw/upstream \
        || git clone git@github.com:eclipse/che-docs.git {toxinidir}/tools/downstreaming-crw/upstream"
    ; update the upstream directory
    bash -c "cd {toxinidir}/tools/downstreaming-crw/upstream \
        && git checkout 7.3.x \
        && git pull --rebase git@github.com:eclipse/che-docs.git 7.3.x --force --prune"
    tox -ecrw-filters

changedir =
    {toxinidir}/tools/downstreaming-crw/

deps =

passenv =
    *

whitelist_externals =
    bash
    tox
    git

Running update

tox -e crw-update
...
Current branch 7.3.x is up to date.
...
crw-filters run-test: commands[1] | /home/remote/ffloreth/src/gl-cee/ffloreth/red-hat-devtools/tools/downstreaming-crw/downstreaming.sh -f
...

⠋ [4] crw-administration-guide | crw-end-user-guide | crw-installation-guide | crw-release_notes
...
✖ FAIL crw-end-user-guide in 7.105 seconds
✔ OK crw-release_notes in 14.164 seconds
✔ OK crw-installation-guide in 15.802 seconds
✔ OK crw-administration-guide in 29.013 seconds
__________________________________________________________________________ summary __________________________________________________________________________
  crw-administration-guide: commands succeeded
ERROR:   crw-end-user-guide: parallel child exit code 1
  crw-installation-guide: commands succeeded
  crw-release_notes: commands succeeded
ERROR: InvocationError for command /home/remote/ffloreth/.local/bin/tox -p auto -e crw-administration-guide,crw-end-user-guide,crw-installation-guide,crw-release_notes (exited with code 1)
__________________________________________________________________________ summary __________________________________________________________________________
ERROR:   crw-filters: commands failed
ERROR: InvocationError for command /home/remote/ffloreth/.local/bin/tox -ecrw-filters (exited with code 1)
__________________________________________________________________________ summary __________________________________________________________________________
ERROR:   crw-update: commands failed

Generating the outline

include::meta/attributes.adoc[]
include::product-meta/product-attributes.adoc[]
include::title-attributes.adoc[]

[id="{title-id}"]
= {title-short}

// This file is generated during downstreaming process.
// Manual modifications are not allowed.

include::topics/crw/end-user-guide/assembly_navigating-che-using-the-dashboard.adoc[leveloffset=+1]
include::topics/crw/end-user-guide/assembly_che-theia-ide-basics.adoc[leveloffset=+1]
include::topics/crw/end-user-guide/con_workspaces-overview.adoc[leveloffset=+1]
include::topics/crw/end-user-guide/assembly_customizing-developer-environments.adoc[leveloffset=+1]

Excerpt from upstream outline

entries:
- title: End-user Guide
  folders:
  - title: "Navigating Che: dashboard"
    url: che-7/navigating-che-using-the-dashboard
    output: web
  - title: Che-Theia IDE basics
    output: web
    folderitems:
    - title: Overview
      url: che-7/che-theia-ide-basics
      output: web
    - title: Defining custom commands for Che-Theia
      url: che-7/defining-custom-commands-for-che-theia
      output: web

Templating with jinja

jinja2 generate-master.j2 \
  upstream/src/main/_data/sidebars/che_7_docs.yml \
  -D target="${GUIDE}" \
  > "${DOWNSTREAM_TITLES}/${GUIDE}/master.adoc"
Excerpt from the jinja template
{%- for guide in entries|sort(attribute='title') -%}
    {%- if guide.title | lower | replace(" ", "-") == target %}
        {%- for chapter in guide.folders -%}
            {%- if chapter.url -%}
                {%- set url = chapter.url | replace('che-7/','') %}
include::topics/crw/{{ target }}/XXX{{ url }}XXX[leveloffset=+1]
            {%- endif -%}
            {%- if chapter.folderitems -%}
                {% for section in chapter.folderitems -%}
                    {%- if section.title == "Overview" -%}
                        {%- set url = section.url | replace('che-7/','') %}
include::topics/crw/{{ target }}/XXX{{ url }}XXX[leveloffset=+1]
                    {%- endif %}
                {%- endfor %}
            {%- endif -%}
        {%- endfor %}
   {%- endif %}
{%- endfor %}

Jekyll headers vs. file name

---
title: Che-Theia IDE basics
keywords:
tags: []
sidebar: che_7_docs
permalink: che-7/che-theia-ide-basics/
folder: che-7/end-user-guide
summary:
---

Finding the file names

grep "permalink:" -T "${UPSTREAM_ROOTDIR}"/src/main/pages/che-7/*/*.adoc \
  | sed 's@^.*7/.*/\(.*\):.*:.*7\/\(.*\)\/@s\@XXX\2XXX \1\@@; s@\ @\@@g; s@.*.html@@' \
  | sort \
  | uniq \
  > filesmap.sed
sed -i -f filesmap.sed \
  "${DOWNSTREAM_TITLES}/${GUIDE}/master.adoc"

Static outline

  • For downstream specific content

// This title is specific to downstream.
// This file is not generated from upstream.
// It is safe to edit this file.
// It is mandatory to modify this file if some topic file has changed on upstream.

[id="{title-id}"]
= {title-short}

include::topics/crw/assembly_installing-codeready-workspaces-on-ocp-4.adoc[leveloffset=+1

Words substitutions

Iteration #1: sed, downstream

#!/bin/sed -i -f

# Remove jekyll headers
1 s/^---$/---===ooo===---/; /---===ooo===---/,/^---$/d

# Cleanup links (must happen before other substitutions)
# In link, remove {site-baseurl}che-7/ and transform into xref.
s@link:\({site-baseurl}che-7/\|#\)\(.*\?\)\[.*\?\]@xref:\2\[\]@
# In xref, remove trailing slash.
s@\(xref:.*\?\)/\[@\1\[@
# In xref, remove directory before dash
s@xref:.*\?/#\?\(.*\?\[\)@xref:\1@

# Replace project name by product name
s/\(Eclipse \)\?\bChe\b$/{prod-short}/g

# Replace project name by product name in the id all references to them
# Yes we have up to 2 occurences of 'che' in an id ...
s/\(\(\[id=\|xref\|<<\).*\)\bche\b/\1{prod-id-short}/g
s/\(\(\[id=\|xref\|<<\).*\)\bche\b/\1{prod-id-short}/g

# Revert back Che-Theia
s/{prod-short}-Theia/Che-Theia/g
s/{prod-id-short}-theia/che-theia/g

# Same with hardcoded result for attributes definitions because they can't include attributes
s/\(:\(parent-\|\)context.*\)\bche\b/\1codeready-workspaces/g
s/\(:\(parent-\|\)context.*\)\bche\b/\1codeready-workspaces/g

# Remove product name where not necessary
s/\({prod-id-short}-\|codeready-workspaces-\|{prod-short} \)\([Ww]\)orkspace/\2orkspace/g

# Replace version numbers
s/\({prod-id-short}-\|{prod-short} \|{prod} \)6/\1{prod-prev-ver}/g
s/\({prod-id-short}-\|{prod-short} \|{prod} \)7/\1{prod-ver}/g

# FIXME (broken, it match too many other things) Cleanup link:#modular-documentation-reference-guide
#s/link:[^#]*#\([^\[]*\)\[/link:#\1[/g

# fix include from overview
s@include::\.\.\/overview@include::\.\./end-user-guide@

# Replace CLI name wisely (not in file names to include)
s/`chectl`/`{prod-cli}`/g
s/\$ chectl/\$ {prod-cli}/g
s/using chectl/using {prod-cli}/g
s/chectl devfile/{prod-cli} devfile/g
s/chectl-generated/{prod-cli}-generated/g

s/\bchectl\b/{prod-cli}/g

# Revert back chectl in file names
s/\(include::.*\?\){prod-cli}/\1chectl/g

# Replace `che` project name
s/`che`/`{prod-projectname}`/g
s/oc project che/oc project {prod-projectname}/g

# Replace `eclipse-che` cluster name
s/`eclipse-che`/`{prod-clustername}`/g

s/ECLIPSE CHE URL/{prod} URL/g

# Replace OpenShift
# s/Open[S|s]hift Container Platform/{ocp}/g
# s/Open[S|s]hift Dedicated/{osd}/g
# s/Open[S|s]hift/OpenShift/g

# Replace Kubernetes (context matters)
s/Kubernetes [a-z]* OpenShift/OpenShift/g
s/Kubernetes/OpenShift/g
# Revert back Kubernetes in AG
s/<4> OpenShift/<4> Kubernetes/g
s/namespace_ (OpenShift)/namespace_ (Kubernetes)/
s/OpenShift Ingresses/Kubernetes Ingresses/

s/kubectl /oc /g

s/Minishift or Minikube/Minikube/g

# Obsolete
# s/[Mm]ulti-user {prod-short}/{prod-short}/g

# EUG: Fix broken xref
s/\(xref:making-a-workspace-portable-using-a-devfile\)\(\[\]\)/\1_crw\2/
s/\(xref:making-a-workspace-portable-using-a-devfile\)_using-developer-environments-workspaces\(\[\]\)/\1_crw\2/
s/\(xref:navigating-{prod-id-short}-using-the-dashboard\)\(\[\]\)/\1_crw\2/
s/\(xref:configuring-a-workspace-using-a-devfile\)\(\[\]\)/\1_crw\2/

# EUG: Remove undesired xref
s/xref:notes\[understood\]/understood/

s/nip.io/mycluster.mycompany.com/g

s/CHE_HOST/CODEREADY_HOST/g
s/CHE_PORT/CODEREADY_PORT/g

# Fix cross guides references: replace xref by a link

#s/the Installation Guide/link:{prod-ig-url}\[the {prod-ig}\]/g

# Fix cross guides references: AG -> EUG
s@\(For a detailed description of the devfile format, see the \)xref:\(making-a-workspace-portable-using-a-devfile_crw\[\)\(\] section\)@\1link:{prod-eug-url}/index#\2Making a workspace portable using a devfile section of the {prod-eug}\]@

# Fix cross guides references: EUG -> IG
s@xref:{prod-id-short}-quick-starts\[\]@link:{prod-ig-url}\[the {prod-ig}\]@
s@xref:running-{prod-id-short}-locally\[\]@link:{prod-ig-url}\[the {prod-ig}\]@
s@xref:installing-the-chectl-management-tool\[\]@link:{prod-ig-url}\[the {prod-ig}\]@

# Fix cross guides references: IG -> AG
s/\(For additional information, see \)xref:\(building-and-running-a-custom-registry-image\[\)\]/\1link:{prod-ag-url}\2the {prod-ag}\]/

# Fix cross guides references: IG -> EUG
s/\(Use the devfile to start the new workspace. See \)xref:\(configuring-a-workspace-using-a-devfile_crw\[\)\]\.\?/\1link:{prod-eug-url}\/index#\2the {prod-eug}\]./
s/\(For each workspace, create a devfile that represent your workspace. See \)xref.*\?\[\]/\1the {prod-eug}/

# Fix cross guides references: IG -> nonexistent (overview)
s/ See xref:{prod-id-short}-plug-in-broker_che-workspace-components\[\].//
s/ See xref:workspace-jwt-proxy_che-workspace-components\[\].//
s/\(To avoid issues when only a subset of the images described below is needed, remove unnecessary plug-ins and devfiles when building custom registries (see \)xref:customizing-the-devfile-and-plug-in-registries\[/\1link:{prod-ag-url}\[the {prod-ag}/

# Remove undesired parts
# EUG: references to local Installation
s/See xref:installing-the-{prod-cli}-management-tool\[/See link:{prod-ig-url}\[the {prod-ig}/
s/follow instructions for xref:uninstalling-{prod-id-short}\[/see link:{prod-ig-url}\[the {prod-ig}/

s/* xref:converting-a-{prod-id-short}-{prod-prev-ver}-workspace-to-a-{prod-id-short}-{prod-ver}-devfile\[\].*/See link:{prod-eug-url}\[the {prod-eug}\]./
s/* xref:configuring-a-workspace-using-a-devfile_crw\[\].*//

# Remove font-awesome instructions
s/(icon:fa-ellipsis-v\[\])//g

s/${CHE_LOCAL_GIT_REPO/$\\{CHE_LOCAL_GIT_REPO/

s/The steps to check are similar to the OpenShift steps. For details, see the .*\? section. The only difference is that, on OpenShift, Ingresses are replaced with routes.//

Iteration #2: attributes upstream

This section describes how to view the state of the {prod-short}
cluster deployment using OpenShift 4 CLI tools.
asciidoc_attributes: &asciidoc_attributes
  project-context: che
  prod: Eclipse Che
  prod-short: Che
  prod-id: eclipse-che
  prod-id-short: che
  prod-prev-ver: 6.x
  prod-ver: 7.3
  prod-cli: chectl
  prod-checluster: eclipse-che
  prod-namespace: default
  prod-url: http(s)://che-host:che-port
  prod-host: che-host
  ocp: OpenShift Container Platform

Iteration #3: enforce attributes upstream

#!/bin/sed -i -f

# Replace long and short project name by an attribute
s/Eclipse Che\b/{prod}/g
s/\bChe\b/{prod-short}/g

# Put back hardcoded project name in Jekyll headers which doesn't understand attributes
# Run twice: we may have 2 occurences in a title
s/\(\(title:\|tags:\)\(.*\?\)\){prod-short}/\1Che/
s/\(\(title:\|tags:\)\(.*\?\)\){prod-short}/\1Che/
s/\(\(title:\|tags:\)\(.*\?\)\){prod}/\1Eclipse Che/
s/\(\(title:\|tags:\)\(.*\?\)\){prod}/\1Eclipse Che/

# Replace project id by an attribute
# Run twice: we may have 2 occurences in the same id
s/\(\(\[id=\|xref\|<<\).*\)\bche\b/\1{prod-id-short}/g
s/\(\(\[id=\|xref\|<<\).*\)\bche\b/\1{prod-id-short}/g

# Replace version numbers
s/\({prod-id-short}-\|{prod-short} \|{prod} \)6/\1{prod-prev-ver}/g
s/\({prod-id-short}-\|{prod-short} \|{prod} \)7/\1{prod-ver}/g

# Revert back baseurl in xref to hardcoded values
s/{site-baseurl}{prod-id-short}-{prod-ver}/{site-baseurl}che-7/g

# Revert back Che-Theia
s/{prod-short}-Theia/Che-Theia/g
s/{prod-id-short}-theia/che-theia/g

Context specific content

Examples

include::examples/{project-context}-build-the-devfile-registry.sh[]
├── administration-guide
│   ├── examples
│   │   ├── che-build-the-devfile-registry.sh
│   │   ├── che-build-the-plug-in-registry.sh
│   │   ├── che-clone-the-devfile-registry-repository.sh
│   │   ├── che-clone-the-plug-in-registry-repository.sh
│   │   ├── che-devfile-meta.yml
│   │   ├── crw-build-the-devfile-registry.sh
│   │   ├── crw-build-the-plug-in-registry.sh
│   │   ├── crw-clone-the-devfile-registry-repository.sh
│   │   ├── crw-clone-the-plug-in-registry-repository.sh
│   │   └── system-variables.adoc

Screenshots

image::workspaces/{project-context}-select-workpace.png[Select a stack from the list]
src/main/images/workspaces/
...
├── che-select-workpace.png

Paragraphs

ifeval::["{project-context}" == "che"]
* For installations that did not use the `-n` argument, the created namespace is named `{prod-id-short}` by default.
endif::[]

ifeval::["{project-context}" == "crw"]
* For installations that did not use the `-n` argument, the created namespace is named `workspaces` by default.
endif::[]

Challenges

Fail faster

Automate feedback