diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 14f897c..c2b5e1d 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -5,15 +5,16 @@ on: [push] jobs: run-ci-linux: runs-on: ubuntu-latest - env: - TYPST_INSTALL: /usr/local - TYPST_VERSION: 0.11.1 steps: - name: Check out repository code uses: actions/checkout@v3 - - name: Install Typst CLI - run: curl -fsSL https://typst.community/typst-install/install.sh | sh -s "$TYPST_VERSION" - - name: Set up Python 3 - run: apt update -y && apt install python3-pip -y + - name: Install dependencies for Nix setup action + run: | + apt update -y + apt install sudo -y + - name: Setup Nix + uses: cachix/install-nix-action@v27 + with: + nix_path: nixpkgs=channel:nixos-unstable - name: Run CI - run: bash -c ./run-ci.sh + run: nix-shell --run ./run-ci.sh diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 39496c8..f2f6809 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -1,28 +1,31 @@ name: release -on: +on: push: tags: - - '*' + - 'v*.*.*' jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Check out repository code + uses: actions/checkout@v3 + - name: Install dependencies for Nix setup action + run: | + apt update -y + apt install sudo -y + - name: Setup Nix + uses: cachix/install-nix-action@v27 with: - fetch-depth: 0 - - name: Install Typst CLI - run: curl -fsSL https://typst.community/typst-install/install.sh | sh -s "$TYPST_VERSION" - - name: Set up Python 3 - run: apt update -y && apt install python3-pip -y + nix_path: nixpkgs=channel:nixos-unstable - name: Run CI - run: bash -c ./run-ci.sh + run: nix-shell --run ./run-ci.sh - name: Setup go uses: https://github.com/actions/setup-go@v4 with: go-version: '>=1.20.1' - - name: Create release + - name: Create release id: create-release uses: https://gitea.com/actions/release-action@main with: diff --git a/generate-theme.sh b/generate-theme.sh old mode 100644 new mode 100755 diff --git a/run-ci.sh b/run-ci.sh index 40d2c65..3afcba3 100755 --- a/run-ci.sh +++ b/run-ci.sh @@ -25,19 +25,29 @@ function enter-section() { printf "\\n" eval "$2" - if [ $? -neq $3 ]; then - abort "command: $2 failed in section: $1" + exit_status=$? + + if [ "$3" == "should fail" ]; then + log "INFO" "expected to fail..." + if [ $exit_status -eq 0 ]; then + abort "command: $2 failed in section: $1 with: $exit_status" + fi + else + log "INFO" "expected to pass..." + if [ ! $exit_status -eq 0 ]; then + abort "command: $2 failed in section: $1 with: $exit_status" + fi fi printf "\\n" log "INFO" "section $1 completed successfully" } -enter-section "BUILD: ABB code theme" "./generate-theme.sh" 0 -enter-section "Compiling template..." "typst compile template/main.typ --root . example.pdf" 0 -enter-section "TEST: local template import" "typst compile tests/local-import/main.typ --root ." 0 -enter-section "TEST: invalid config case 1" "typst compile tests/invalid-config/test-case-1.typ --root ." 1 -enter-section "TEST: invalid config case 2" "compile tests/invalid-config/test-case-2.typ" 0 -enter-section "TEST: invalid config case 3" "typst compile tests/invalid-config/test-case-3.typ --root ." 1 +enter-section "Typstyle checking" "./run-fmt.sh --check src/lib.typ" 0 +enter-section "Compiling template..." "typst compile template/main.typ --root . example.pdf" +enter-section "TEST: local template import" "typst compile tests/local-import/main.typ --root ." +enter-section "TEST: invalid config case 1" "typst compile tests/invalid-config/test-case-1.typ --root ." "should fail" +enter-section "TEST: invalid config case 2" "typst compile tests/invalid-config/test-case-2.typ --root ." +enter-section "TEST: invalid config case 3" "typst compile tests/invalid-config/test-case-3.typ --root ." "should fail" log "INFO" "CI completed successfully" diff --git a/run-fmt.sh b/run-fmt.sh new file mode 100755 index 0000000..b4fee1c --- /dev/null +++ b/run-fmt.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +function format() { + # format file + # typstyle --format $1 + + if [ -z "$1" ]; then + return + fi + + local wd=$(dirname $(realpath "$1")) + + echo "processing file $1..." + typstyle "$2" "$1" > /dev/null + if [ $? -eq 1 ]; then + echo "failed format validation: $1" + exit 1 + fi + + local imports=$(rg "#import \"([a-z0-9/\-]+\.typ)\"" -Nor '$1' "$1") + + # format all included files + while IFS= read -r line; do + if [ -z "$line" ]; then + continue + fi + format "$wd/$line" "$2" + done <<< "$imports" +} + +case $1 in + "--format") + format "$2" "--inplace" + ;; + "--check") + format "$2" "--check" + ;; + *) + echo "unknown option: $1" + exit 1 + ;; +esac diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..a71ba0e --- /dev/null +++ b/shell.nix @@ -0,0 +1,13 @@ +let + nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-unstable"; + pkgs = import nixpkgs { config = {}; overlays = []; }; +in + +pkgs.mkShellNoCC { + packages = with pkgs; [ + python312 + python312Packages.pyyaml + typst + typstyle + ]; +} diff --git a/src/glossarium.typ b/src/glossarium.typ index 5b1a8de..7589b4a 100644 --- a/src/glossarium.typ +++ b/src/glossarium.typ @@ -30,7 +30,10 @@ SOFTWARE.*/ #let __query_labels_with_key(loc, key, before: false) = { if before { query( - selector(label(__glossary_label_prefix + key)).before(loc, inclusive: false), + selector(label(__glossary_label_prefix + key)).before( + loc, + inclusive: false, + ), loc, ) } else { @@ -56,13 +59,18 @@ SOFTWARE.*/ let entlong = entry.at("long", default: "") let textLink = if display != none { [#display] - } else if (is_first or long == true) and entlong != [] and entlong != "" and long != false { + } else if ( + is_first or long == true + ) and entlong != [] and entlong != "" and long != false { [#entlong (#entry.short#suffix)] } else { [#entry.short#suffix] } - [#link(label(entry.key), textLink)#label(__glossary_label_prefix + entry.key)] + [#link( + label(entry.key), + textLink, + )#label(__glossary_label_prefix + entry.key)] } else { panic(__not-found-panic-error-msg(key)) } @@ -100,13 +108,18 @@ SOFTWARE.*/ [#entplural] } - let textLink = if (is_first or long == true) and entlong != [] and entlong != "" and long != false { + let textLink = if ( + is_first or long == true + ) and entlong != [] and entlong != "" and long != false { [#entlong (#short)] } else { [#short] } - [#link(label(entry.key), textLink)#label(__glossary_label_prefix + entry.key)] + [#link( + label(entry.key), + textLink, + )#label(__glossary_label_prefix + entry.key)] } else { panic(__not-found-panic-error-msg(key)) } @@ -116,7 +129,9 @@ SOFTWARE.*/ // show rule to make the references for glossarium #let make-glossary(body) = { show ref: r => { - if r.element != none and r.element.func() == figure and r.element.kind == __glossarium_figure { + if r.element != none and r.element.func() == figure and r + .element + .kind == __glossarium_figure { // call to the general citing function gls(str(r.target), suffix: r.citation.supplement) } else { @@ -200,7 +215,9 @@ SOFTWARE.*/ .sorted(key: x => x.page()) .fold( (values: (), pages: ()), - ((values, pages), x) => if pages.contains(x.page()) { + ((values, pages), x) => if pages.contains( + x.page(), + ) { (values: values, pages: pages) } else { values.push(x) @@ -214,7 +231,10 @@ SOFTWARE.*/ if page-numbering == none { page-numbering = "1" } - link(x)[#numbering(page-numbering, ..counter(page).at(x))] + link(x)[#numbering( + page-numbering, + ..counter(page).at(x), + )] } ) .join(", ") diff --git a/src/glossary.typ b/src/glossary.typ index 82b8924..8cbb8c3 100644 --- a/src/glossary.typ +++ b/src/glossary.typ @@ -75,8 +75,14 @@ // create dedicated entries for // acronym and glossary if "long" in v and "desc" in v { - processed_glossary.insert(k, (short: v.short, long: v.long, group: acronym_group)) - processed_glossary.insert(k + "__glossary_entry", (short: v.short, desc: v.desc, long: v.long, group: glossary_group)) + processed_glossary.insert( + k, + (short: v.short, long: v.long, group: acronym_group), + ) + processed_glossary.insert( + k + "__glossary_entry", + (short: v.short, desc: v.desc, long: v.long, group: glossary_group), + ) } else { processed_glossary.insert(k, v) processed_glossary.at(k).group = group diff --git a/src/lib.typ b/src/lib.typ index 348265e..4e2779e 100644 --- a/src/lib.typ +++ b/src/lib.typ @@ -31,7 +31,13 @@ inset: (left: 2pt, right: 2pt), outset: (top: 4pt, bottom: 4pt), fill: ABB-GRAY-06, - [#box(fill: rgb(color), radius: 2pt, inset: 0pt, width: 0.75em, height: 0.75em) #text( + [#box( + fill: rgb(color), + radius: 2pt, + inset: 0pt, + width: 0.75em, + height: 0.75em, + ) #text( font: default-config.style.code.font, size: default-config.style.code.size, content, @@ -40,13 +46,17 @@ } #let url(label, content) = { - link(label)[#underline(offset: 2pt, stroke: 0.5pt + blue, text(fill: blue)[ - #content - #let domain = label.find(regex("\w+\.\w+(?:\.\w+)*")) - #if domain.len() > 0 [ - (#domain) - ] - ])] + link(label)[#underline( + offset: 2pt, + stroke: 0.5pt + blue, + text(fill: blue)[ + #content + #let domain = label.find(regex("\w+\.\w+(?:\.\w+)*")) + #if domain.len() > 0 [ + (#domain) + ] + ], + )] } // start of template pages and styles diff --git a/src/pages/confidentiality-statement.typ b/src/pages/confidentiality-statement.typ index 5521c05..8a6c272 100644 --- a/src/pages/confidentiality-statement.typ +++ b/src/pages/confidentiality-statement.typ @@ -43,11 +43,11 @@ set par(justify: true) - if text.lang == "de" [ - darf weder als Ganzes noch in Auszügen Personen außerhalb des Prüfungsprozesses und des Evaluationsverfahrens zugänglich gemacht werden, sofern keine anderslautende Genehmigung der Ausbildungsstätte vorliegt. - ] else if text.lang == "en" [ - may not be made accessible to persons outside the examination process and the evaluation procedure, either as a whole or in excerpts, unless otherwise authorized by the training institution. - ] + if text.lang == "de" [ + darf weder als Ganzes noch in Auszügen Personen außerhalb des Prüfungsprozesses und des Evaluationsverfahrens zugänglich gemacht werden, sofern keine anderslautende Genehmigung der Ausbildungsstätte vorliegt. + ] else if text.lang == "en" [ + may not be made accessible to persons outside the examination process and the evaluation procedure, either as a whole or in excerpts, unless otherwise authorized by the training institution. + ] set align(horizon) diff --git a/src/pages/titlepage.typ b/src/pages/titlepage.typ index 39db53f..5d33a54 100644 --- a/src/pages/titlepage.typ +++ b/src/pages/titlepage.typ @@ -64,7 +64,9 @@ stroke: none, [*Verfasser:*], author.name, [*Bearbeitungszeitraum:*], thesis.timeframe, - [*Matrikelnummer, Kurs:*], str(author.matriculation-number) + ", " + author.course, + [*Matrikelnummer, Kurs:*], + str(author.matriculation-number) + ", " + author.course, + [*Ausbildungsbetrieb:*], author.company, [*Betrieblicher Betreuer:*], author.supervisor, [*Abgabedatum:*], thesis.submission-date, @@ -77,7 +79,9 @@ stroke: none, [*Author:*], author.name, [*Editing period:*], thesis.timeframe, - [*Matriculation number, course:*], str(author.matriculation-number) + ", " + author.course, + [*Matriculation number, course:*], + str(author.matriculation-number) + ", " + author.course, + [*Training company:*], author.company, [*Company supervisor:*], author.supervisor, [*Submission date:*], thesis.submission-date, diff --git a/src/style.typ b/src/style.typ index ae015b5..31b8a57 100644 --- a/src/style.typ +++ b/src/style.typ @@ -12,7 +12,13 @@ #let watermark-color = luma(50%).transparentize(70%) #let watermark-pattern = pattern(size: (5pt, 5pt))[ - #place(line(start: (50%, 0%), end: (50%, 100%), stroke: (paint: watermark-color, thickness: 3pt))) + #place( + line( + start: (50%, 0%), + end: (50%, 100%), + stroke: (paint: watermark-color, thickness: 3pt), + ), + ) ] #let watermark(config) = if config.draft { @@ -29,7 +35,10 @@ #linebreak() document version. #linebreak() - #text(size: 0.75em, "Further usage without the authors consent is not permitted.")]]] + #text( + size: 0.75em, + "Further usage without the authors consent is not permitted.", + )]]] } #let numberingH(c) = { @@ -117,37 +126,51 @@ e => { let (i, l) = e let n = i + 1 - let n_str = if (calc.rem(n, 1) == 0) or (true and i == 0) { text(font: style.code.font, size: style.code.size, fill: ABB-BLACK, str(n)) } else { none } - (n_str + h(0.5em), raw(block: true, lang: lang, l)) - }) - } - else { - ( ( 1fr, ), - ( left, ), - e => { - let (i, l) = e - raw(block: true, lang: lang, l) - } + let n_str = if (calc.rem(n, 1) == 0) or (true and i == 0) { + text( + font: style.code.font, + size: style.code.size, + fill: ABB-BLACK, + str(n), ) - } + } else { + none + } + (n_str + h(0.5em), raw(block: true, lang: lang, l)) + }, + ) + } else { + ( + (1fr,), + (left,), + e => { + let (i, l) = e + raw(block: true, lang: lang, l) + }, + ) } - grid( - stroke: none, - columns: columns, - rows: (auto,), - gutter: 0pt, - inset: 0.25em, - align: (col, _) => align.at(col), - fill: ABB-GRAY-06, - ..content - .text - .split("\n") - .enumerate() - .map(make_row) - .flatten() - .map(c => if c.has("text") and c.text == "" { v(1em) } else { c }) - ) - } + } + grid( + stroke: none, + columns: columns, + rows: (auto,), + gutter: 0pt, + inset: 0.25em, + align: (col, _) => align.at(col), + fill: ABB-GRAY-06, + ..content + .text + .split("\n") + .enumerate() + .map(make_row) + .flatten() + .map(c => if c.has("text") and c.text == "" { + v(1em) + } else { + c + }) + ) + }, ) #v(-1em) #align(center + top, it.caption) @@ -184,8 +207,20 @@ stroke: (x, y) => ( left: none, right: none, - top: if y == 0 { 1.5pt } else if y < 2 { 1pt } else { 0.5pt }, - bottom: if y == 0 { 1pt } else { 1.5pt } )) + top: if y == 0 { + 1.5pt + } else if y < 2 { + 1pt + } else { + 0.5pt + }, + bottom: if y == 0 { + 1pt + } else { + 1.5pt + }, + ), + ) // make table header bold show table.cell.where(y: 0): set text(weight: "bold") @@ -194,7 +229,8 @@ set par( justify: true, first-line-indent: 1em, - leading: 1em) + leading: 1em, + ) // give links a color show link: set text(fill: style.link.color) @@ -207,17 +243,26 @@ header-ascent: style.header.content-padding, footer-descent: style.header.content-padding, margin: ( - top: style.page.margin.top + style.header.underline-top-padding + style.header.content-padding, + top: style.page.margin.top + style.header.underline-top-padding + style + .header + .content-padding, bottom: style.page.margin.bottom + style.footer.content-padding, left: style.page.margin.left, - right: style.page.margin.right), + right: style.page.margin.right, + ), numbering: (..nums) => { let current-page = here().page() - if current-page == 1{ + if current-page == 1 { [] - } else if query().first().location().page() > current-page { + } else if query() + .first() + .location() + .page() > current-page { numbering("I", nums.pos().first()) - } else if query().first().location().page() >= current-page { + } else if query() + .first() + .location() + .page() >= current-page { numbering("1", nums.pos().first()) } else { numbering("a", nums.pos().first()) @@ -234,7 +279,11 @@ set align(center) numbering("I", page-counter) } else if query().first().location().page() >= page-number { - numbering("1 / 1", page-counter, counter(page).at().last()) + numbering( + "1 / 1", + page-counter, + counter(page).at().last(), + ) } else { numbering("a", page-counter) } @@ -254,7 +303,13 @@ // right align logo of DHBW align(right, image("res/DHBW.svg", height: style.header.logo-height))) - } else if query().first().location().page() >= current-page and query().first().location().page() < current-page + 1 { + } else if query() + .first() + .location() + .page() >= current-page and query() + .first() + .location() + .page() < current-page + 1 { let heading = currentH() heading.at(0) @@ -267,7 +322,8 @@ v(style.header.underline-top-padding - 1em) line(length: 100%) } - }) + }, + ) body } diff --git a/tests/invalid-config/test-case-1.typ b/tests/invalid-config/test-case-1.typ index 92d7098..5059966 100644 --- a/tests/invalid-config/test-case-1.typ +++ b/tests/invalid-config/test-case-1.typ @@ -1,8 +1,7 @@ #import "../../src/lib.typ": dhbw-template -#show: dhbw-template.with( - config: ( +#show: dhbw-template.with(( lang: none, region: "en", author: ( diff --git a/tests/invalid-config/test-case-2.typ b/tests/invalid-config/test-case-2.typ index 10793ca..8a6eb26 100644 --- a/tests/invalid-config/test-case-2.typ +++ b/tests/invalid-config/test-case-2.typ @@ -1,30 +1,34 @@ #import "../../src/lib.typ": dhbw-template -#show: dhbw-template.with( - config: ( - lang: "en", - this-key-is-not-in-config: "Ha Ha", - region: "en", - author: ( - name: "Sven Vogel", - semester: 4, - program: "Informationtechnology", - course: "TINF19IT1", - faculty: "Technik", - university: "DHBW Mannheim", - company: "ABB AG", - supervisor: "Benny Goodman", - matriculation-number: 123456789), - thesis: ( - title: "Unofficial ABB/DHBW Typst template", - subtitle: "for reports and thesises", - submission-date: "23rd march 2020", - timeframe: "1st january 2020 - 20th march 2020", - kind: "T2000", - summary: none, - abstract: none, - keywords: ( "IT", "other stuff" ), - bibliography: none, - glossary: none, - appendices: none))) +#show: dhbw-template.with(( + lang: "en", + this-key-is-not-in-config: "Ha Ha", + region: "en", + author: ( + name: "Sven Vogel", + semester: 4, + program: "Informationtechnology", + course: "TINF19IT1", + faculty: "Technik", + university: "DHBW Mannheim", + company: "ABB AG", + supervisor: "Benny Goodman", + matriculation-number: 123456789, + ), + thesis: ( + title: "Unofficial ABB/DHBW Typst template", + subtitle: "for reports and thesises", + submission-date: "23rd march 2020", + timeframe: "1st january 2020 - 20th march 2020", + kind: "T2000", + summary: none, + abstract: none, + keywords: ("IT", "other stuff"), + bibliography: none, + glossary: none, + appendices: none, + ), +)) + += Heading diff --git a/tests/invalid-config/test-case-3.typ b/tests/invalid-config/test-case-3.typ index ef42dd8..f274b60 100644 --- a/tests/invalid-config/test-case-3.typ +++ b/tests/invalid-config/test-case-3.typ @@ -1,24 +1,26 @@ #import "../../src/lib.typ": dhbw-template -#show: dhbw-template.with( - config: ( - lang: "en", - region: "en", - author: ( - university: "DHBW Mannheim", - company: "ABB AG", - supervisor: none, - matriculation-number: 123456789), - thesis: ( - title: "Unofficial ABB/DHBW Typst template", - subtitle: "for reports and thesises", - submission-date: "23rd march 2020", - timeframe: "1st january 2020 - 20th march 2020", - kind: "T2000", - summary: none, - abstract: none, - keywords: ( "IT", "other stuff" ), - bibliography: none, - glossary: none, - appendices: none))) +#show: dhbw-template.with(( + lang: "en", + region: "en", + author: ( + university: "DHBW Mannheim", + company: "ABB AG", + supervisor: none, + matriculation-number: 123456789, + ), + thesis: ( + title: "Unofficial ABB/DHBW Typst template", + subtitle: "for reports and thesises", + submission-date: "23rd march 2020", + timeframe: "1st january 2020 - 20th march 2020", + kind: "T2000", + summary: none, + abstract: none, + keywords: ("IT", "other stuff"), + bibliography: none, + glossary: none, + appendices: none, + ), +)) diff --git a/tests/local-import/main.typ b/tests/local-import/main.typ index 9197122..dd8fb27 100644 --- a/tests/local-import/main.typ +++ b/tests/local-import/main.typ @@ -1,8 +1,7 @@ #import "../../src/lib.typ": dhbw-template -#show: dhbw-template.with( - config: ( +#show: dhbw-template.with(( lang: "en", region: "en", author: ( @@ -27,3 +26,5 @@ bibliography: none, glossary: none, appendices: none))) + += Heading