Skip to content

Instantly share code, notes, and snippets.

@ogerardin
Created December 8, 2023 13:01
Show Gist options
  • Save ogerardin/bb69291a18b8203f0d6d796222ebc4dc to your computer and use it in GitHub Desktop.
Save ogerardin/bb69291a18b8203f0d6d796222ebc4dc to your computer and use it in GitHub Desktop.
Export Archi collaborative model to Confluence page
#! /bin/bash
# This script will export the views from an Archi collaborative model and upload them to a Confluence page.
# Usage: updateConfluence.sh [ DIR ]
# where DIR is base directory of the Archi model repo. If it is not specified, it defaults to the parent directory of
# this script (which reflects this repo's structure).
#
# Requirements: curl, yq, xmllint, git
#
# Ref:
# Archi CLI doc: https://github.com/archimatetool/archi/wiki/Archi-Command-Line-Interface
# Confluence API doc: https://docs.atlassian.com/ConfluenceServer/rest/7.19.16/
# Confluence storage format: https://confluence.atlassian.com/conf719/confluence-storage-format-1157466554.html
# Confluence base URI
BASEURL=https://xxxxxx
# ID of target Confluence page (must exist beforehand)
PAGEID=xxxxxx
# Personal access token for Confluence API authentication, manage here: https://confluence.intramundi.com/plugins/personalaccesstokens/usertokens.action
TOKEN=xxxxxx
# Where the HTML files will go
HTMLDIR=output/html
# Where the reports will go
REPORTDIR=output/report
uploadAttachment() {
local -r file=$1
local -r basename=$(basename "$file")
# query attachment
result=$(curl -k --no-progress-meter --location "$BASEURL/rest/api/content/$PAGEID/child/attachment?filename=$basename&expand=version" \
--header "Authorization: Bearer $TOKEN") || return 1
size=$(echo "$result" | jq -r ".size")
if [ $size -ne 0 ]; then
attachmentId=$(echo "$result" | jq -r ".results[0].id")
version=$(echo "$result" | jq ".results[0].version.number")
echo " attachment exists (id=$attachmentId, version=$version) - updating"
result=$(curl -k --no-progress-meter --location "$BASEURL/rest/api/content/$PAGEID/child/attachment/$attachmentId/data" \
--header "X-Atlassian-Token: nocheck" \
--header "Authorization: Bearer $TOKEN" \
--form "file=@$file") || return 1
version=$(echo "$result" | jq ".version.number")
echo " new version: $version"
else
echo " new attachment - creating"
result=$(curl -k --no-progress-meter --location "$BASEURL/rest/api/content/$PAGEID/child/attachment" \
--header "X-Atlassian-Token: nocheck" \
--header "Authorization: Bearer $TOKEN" \
--form "file=@$file") || return 1
attachmentId=$(echo "$result" | jq -r ".results[0].id")
version=$(echo "$result" | jq ".results[0].version.number")
echo " attachment created (id=$attachmentId, version=$version)"
fi
}
updateBody() {
echo " Querying page $PAGEID"
result=$(curl -k --no-progress-meter --location "$BASEURL/rest/api/content/$PAGEID?expand=body.view,version" --header "Authorization: Bearer $TOKEN") || return 1
version=$(echo "$result" | jq ".version.number")
title=$(echo "$result" | jq -r ".title")
# body=$(echo "$result" | jq -r ".body.view.value")
echo " version=$version, title=$title"
echo " Updating body"
((version++))
result=$(curl -k --no-progress-meter --location --request PUT "$BASEURL/rest/api/content/$PAGEID" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer $TOKEN" \
--data "{
\"version\": {
\"number\": $version,
\"message\": \"Generated by $0\"
},
\"type\": \"page\",
\"title\": \"$title\",
\"body\": {
\"storage\": {
\"value\": $(echo "$BODY" | jq -Rsa .),
\"representation\": \"storage\"
}
}
}") || return 1
echo " new version: $version"
}
processFolder() {
local -r folder=$1
local -r level=$2
local -r imgFolder=$3
folderName=$(xmllint --xpath 'string(//@name)' "$folder/folder.xml") || return 1
echo "Found folder '$folderName' (level $level)"
BODY="$BODY<h$level>$folderName</h$level>"
viewLevel=$((level + 1))
shopt -s nullglob #no iteration if no file matched
shopt -s lastpipe #make sure assignment works in pipeline
# Iterate on views in folder, sorted by view name
for f in "$folder"/ArchimateDiagramModel_*.xml; do
viewName=$(xmllint --xpath 'string(//@name)' "$f")
id=$(xmllint --xpath 'string(//@id)' "$f")
echo "$id" "$viewName"
done | sort -k2 | while read -r id viewName; do
echo "[$level] Found diagram '$viewName' (id: $id)"
#image file name matches the view ID
img=$imgFolder/$id.png
[ -f "$img" ] || { echo "image not found $img"; continue; }
echo "[$level] Found image: $(file "$img")"
echo "[$level] Attaching image to page $PAGEID"
uploadAttachment "$img" || return 2
BODY="$BODY<h$viewLevel>$viewName</h$viewLevel><ac:image ac:border='true'><ri:attachment ri:filename=\"$(basename "$img")\"/></ac:image><br/>"
# break
done
# process subfolders
shopt -s nullglob
for d in "$folder"/id-*/; do
[ -d "$d" ] && processFolder "$d" $((level + 1)) "$imgFolder" || return 3
done
}
processModel() {
local folder=$1
local htmlBase=$2
modelId=$(xmllint --xpath 'string(//*/@id)' "$folder/folder.xml") || return 1
modelName=$(xmllint --xpath 'string(//*/@name)' "$folder/folder.xml") || return 1
echo "Found model '$modelName' (id: $modelId)"
processFolder "$folder/diagrams" 1 "$htmlBase/$modelId/images" || return 2
}
#
# main
#
if [ -n "$1" ] ; then
cd "$1" || exit 1
else
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd "$SCRIPT_DIR"/.. || exit 1
fi
echo "Started $(date -Isecond)"
echo "Working directory: $(pwd)"
if [ ! -f ./model/folder.xml ] ; then
echo "No Archi model found in $(pwd)"
exit 2
fi
echo "Generating Archi HTML UI in $HTMLDIR..."
Archi -application com.archimatetool.commandline.app -consoleLog -nosplash --modelrepository.loadModel . \
--html.createReport $HTMLDIR || exit 3
echo "Generating Archi PDF report in $REPORTDIR..."
Archi -application com.archimatetool.commandline.app -consoleLog -nosplash --modelrepository.loadModel . \
--jasper.filename report -jasper.format PDF --jasper.title "ArchiMate report" --jasper.createReport $REPORTDIR || exit 4
#initialize page top
remote=$(git remote get-url origin)
case $remote in
git@*)
# rewrite SSH URL as HTTP
remote=${remote#git@}
remote=${remote%.git}
remote=${remote//:/\/}
remote=https://$remote
;;
http*)
#remove credentials
[[ $remote =~ (https?://)(.+@)?(.+) ]] && remote="${BASH_REMATCH[1]}${BASH_REMATCH[3]}"
;;
esac
BODY=
BODY=$BODY"<ac:structured-macro ac:name=\"info\">
<ac:rich-text-body>
<p><b>This page was generated automatically from Archi model in repo <a href=\"$remote\">$remote</a> on $(date)</b></p>
<p><b>Do not edit manually!</b></p>
</ac:rich-text-body>
</ac:structured-macro>"
BODY=$BODY"<ac:structured-macro ac:name=\"toc\"/>"
echo "Attaching PDF report to page $PAGEID"
pdf=$REPORTDIR/report.pdf
uploadAttachment "$pdf" || exit 5
BODY="$BODY<p>Full report: <ac:link><ri:attachment ri:filename=\"$(basename "$pdf")\"/></ac:link></p>"
processModel ./model $HTMLDIR || exit 6
echo "Udpating page $PAGEID"
#echo "$BODY" > body.xhtml
updateBody || exit 7
echo "Done $(date -Isecond)"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment