Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Adding automated PDF building
  • Loading branch information
wpotrzebowski committed Jul 20, 2026
commit f2f4130ccc4792bb44f6cedaaef7dbd406167fe2
146 changes: 146 additions & 0 deletions .github/workflows/fodt-to-pdf.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
name: Convert FODT to PDF

# Convert newly added/changed .fodt tutorials to PDF with LibreOffice,
# then commit the generated PDFs back to the same branch.
on:
push:
paths:
- "**.fodt"
workflow_dispatch:
inputs:
all_fodt:
description: "Convert every tracked .fodt file (not only files from the latest commit)"
type: boolean
default: true

permissions:
contents: write

concurrency:
group: fodt-to-pdf-${{ github.ref }}
cancel-in-progress: true

jobs:
convert:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Need parent commit to diff changed files on push.
fetch-depth: 2
# Check out the branch that triggered the workflow so we can push back.
ref: ${{ github.ref }}

- name: Install LibreOffice
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libreoffice-writer \
default-jre-headless
soffice --version

- name: Collect FODT files to convert
id: fodt
shell: bash
run: |
set -euo pipefail

if [[ "${{ github.event_name }}" == "push" ]]; then
BEFORE="${{ github.event.before }}"
if [[ -z "$BEFORE" || "$BEFORE" =~ ^0+$ ]]; then
mapfile -t FILES < <(git ls-files '*.fodt')
else
mapfile -t FILES < <(git diff --name-only --diff-filter=AM "$BEFORE" "${{ github.sha }}" -- '*.fodt' || true)
fi
elif [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.all_fodt }}" != "true" ]]; then
# Manual run without "all": convert files changed in the latest commit.
mapfile -t FILES < <(git diff --name-only --diff-filter=AM HEAD~1 HEAD -- '*.fodt' || true)
else
# workflow_dispatch with all_fodt=true (or fallback): convert everything.
mapfile -t FILES < <(git ls-files '*.fodt')
fi

# Drop empty entries
FILES=("${FILES[@]}")
COUNT=0
: > /tmp/fodt_list.txt
for f in "${FILES[@]+"${FILES[@]}"}"; do
[[ -z "${f:-}" ]] && continue
[[ -f "$f" ]] || continue
echo "$f" >> /tmp/fodt_list.txt
COUNT=$((COUNT + 1))
done

echo "count=$COUNT" >> "$GITHUB_OUTPUT"
{
echo "files<<EOF"
cat /tmp/fodt_list.txt
echo "EOF"
} >> "$GITHUB_OUTPUT"

echo "FODT files to convert ($COUNT):"
cat /tmp/fodt_list.txt || true

- name: Convert FODT to PDF
if: steps.fodt.outputs.count != '0'
shell: bash
run: |
set -euo pipefail
chmod +x build_tools/convert_fodt_to_pdf.sh build_tools/build_pdfs.sh
mapfile -t FILES < /tmp/fodt_list.txt
./build_tools/build_pdfs.sh "${FILES[@]}"
echo "Generated PDFs:"
while IFS= read -r fodt; do
pdf="${fodt%.fodt}.pdf"
ls -lh "$pdf"
done < /tmp/fodt_list.txt

- name: Stage PDF artifacts
if: steps.fodt.outputs.count != '0'
shell: bash
run: |
set -euo pipefail
mkdir -p /tmp/pdf-artifacts
while IFS= read -r fodt; do
pdf="${fodt%.fodt}.pdf"
if [[ -f "$pdf" ]]; then
# Preserve relative path under the artifact root.
mkdir -p "/tmp/pdf-artifacts/$(dirname "$pdf")"
cp "$pdf" "/tmp/pdf-artifacts/$pdf"
fi
done < /tmp/fodt_list.txt
find /tmp/pdf-artifacts -type f -name '*.pdf' -ls

- name: Upload PDF artifacts
if: steps.fodt.outputs.count != '0'
uses: actions/upload-artifact@v4
with:
name: tutorial-pdfs
path: /tmp/pdf-artifacts
if-no-files-found: warn
retention-days: 14

- name: Commit and push PDFs
if: steps.fodt.outputs.count != '0' && github.event_name != 'pull_request'
shell: bash
run: |
set -euo pipefail

while IFS= read -r fodt; do
pdf="${fodt%.fodt}.pdf"
if [[ -f "$pdf" ]]; then
git add -- "$pdf"
fi
done < /tmp/fodt_list.txt

if git diff --cached --quiet; then
echo "No PDF changes to commit"
exit 0
fi

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -m "Auto-convert tutorial FODT to PDF"
git push
52 changes: 30 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
# tutorials
This repository is where contributed tutorials for SasView live!

.fodt files placed here are grabbed by Jenkins and built into pdf
at https://jenkins.esss.dk/sasview/job/SasView_Tutorials/
`.fodt` files committed here are automatically converted to PDF by GitHub
Actions (`.github/workflows/fodt-to-pdf.yml`) using LibreOffice. The generated
PDFs are committed back to the same branch and also uploaded as workflow
artifacts.

Once any new/changed tutorials are ready for general consumption the
To convert locally (requires LibreOffice):

```bash
./build_tools/build_pdfs.sh # all tracked .fodt files
./build_tools/build_pdfs.sh path/to/file.fodt
./build_tools/build_pdfs.sh --root # root-level .fodt only
```

Once any new/changed tutorials are ready for general consumption the
following steps should be taken:

- All the pdf files should be copied to the /downloads folder of the
website repository sasview.github.io. This is effectively the
release step for the tutorials, making them available from
- All the pdf files should be copied to the /downloads folder of the
website repository sasview.github.io. This is effectively the
release step for the tutorials, making them available from
http://www.sasview.org/links.html

- For consistency, and to avoid rendering issues, the pdfs should
be downloaded from Jenkins AND NOT generated locally in LibreOffice,
etc.

- Note that technically only those pdfs that have changed (or which

- For consistency, and to avoid rendering issues, prefer the PDFs
produced by the GitHub Action rather than ad-hoc local exports.

- Note that technically only those pdfs that have changed (or which
are new) need to be copied.
- In order to make the new/changed tutorials available from the
SasView help documentation in the next release (and the developer

- In order to make the new/changed tutorials available from the
SasView help documentation in the next release (and the developer
builds) the following two steps need to be taken:
- The pdf files above need to be copied to the /src/sas/sasview/media

- The pdf files above need to be copied to the /src/sas/sasview/media
folder of the SasView repository and committed or git added.
- If any new pdf tutorial (ie, a new .fodt) was created then the
SasView documentation index needs to be updated by appropriately
editing the /docs/sphinx-docs/source/user/tutorial.rst file in the

- If any new pdf tutorial (ie, a new .fodt) was created then the
SasView documentation index needs to be updated by appropriately
editing the /docs/sphinx-docs/source/user/tutorial.rst file in the
SasView repository

34 changes: 34 additions & 0 deletions build_tools/build_pdfs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# Build PDF tutorials from Flat ODT (.fodt) sources using LibreOffice.
#
# Usage:
# ./build_tools/build_pdfs.sh # all tracked *.fodt files
# ./build_tools/build_pdfs.sh path/to/a.fodt # specific file(s)
# ./build_tools/build_pdfs.sh --root # root-level *.fodt only
#
# CI entry point: .github/workflows/fodt-to-pdf.yml
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CONVERT="$ROOT_DIR/build_tools/convert_fodt_to_pdf.sh"

cd "$ROOT_DIR"
chmod +x "$CONVERT"

if [[ "${1:-}" == "--root" ]]; then
shift
shopt -s nullglob
files=(./*.fodt)
if [[ ${#files[@]} -eq 0 ]]; then
echo "No root-level .fodt files found"
exit 0
fi
echo "Converting ${#files[@]} root-level FODT file(s) to PDF"
"$CONVERT" "${files[@]}"
elif [[ $# -gt 0 ]]; then
echo "Converting $# FODT file(s) to PDF"
"$CONVERT" "$@"
else
echo "Converting all tracked FODT files to PDF"
"$CONVERT"
fi
49 changes: 49 additions & 0 deletions build_tools/convert_fodt_to_pdf.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
# Convert one or more Flat ODT (.fodt) tutorials to PDF with LibreOffice.
# Usage:
# convert_fodt_to_pdf.sh path/to/file.fodt [...]
# convert_fodt_to_pdf.sh # convert all tracked *.fodt files
set -euo pipefail

if ! command -v soffice >/dev/null 2>&1 && ! command -v libreoffice >/dev/null 2>&1; then
echo "error: LibreOffice (soffice) is not installed" >&2
exit 1
fi

SOFFICE=(soffice)
if ! command -v soffice >/dev/null 2>&1; then
SOFFICE=(libreoffice)
fi

convert_one() {
local fodt="$1"
if [[ ! -f "$fodt" ]]; then
echo "warning: skipping missing file: $fodt" >&2
return 0
fi
if [[ "$fodt" != *.fodt ]]; then
echo "warning: skipping non-.fodt file: $fodt" >&2
return 0
fi

local dir
dir="$(dirname "$fodt")"
echo "Converting $fodt -> ${dir}/$(basename "${fodt%.fodt}.pdf")"
"${SOFFICE[@]}" --headless --nologo --nofirststartwizard --norestore \
--convert-to pdf --outdir "$dir" "$fodt"
}

if [[ $# -eq 0 ]]; then
mapfile -t files < <(git ls-files '*.fodt' 2>/dev/null || ls -1 ./*.fodt 2>/dev/null || true)
if [[ ${#files[@]} -eq 0 ]]; then
echo "No .fodt files to convert"
exit 0
fi
for fodt in "${files[@]}"; do
convert_one "$fodt"
done
else
for fodt in "$@"; do
convert_one "$fodt"
done
fi
4 changes: 0 additions & 4 deletions build_tools/jenkins_build.sh

This file was deleted.