-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpkgdown.qmd
971 lines (678 loc) · 34 KB
/
pkgdown.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
# pkgdown {#sec-pkgdown}
```{r}
#| eval: true
#| echo: false
#| include: false
source("_common.R")
library(pak)
```
```{r}
#| label: co_box_dev
#| echo: false
#| results: asis
#| eval: false
co_box(
color = "y",
look = "default",
header = "Caution",
contents = "The contents for this section are being developed. Thank you for your patience."
)
```
```{r}
#| label: co_box_tldr
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "b",
look = "default", hsize = "1.10", size = "1.05",
header = "TLDR   ![](images/pkgdown.png){width='50' height='55'}",
fold = TRUE,
contents = "
The `pkgdown` package helps us easily create websites to enhance the visibility and usability of our app-package through a professional and informative website.
- Install `pkgdown`
- Run `usethis::use_pkgdown_github_pages()` to set up the package and writes the configuration file (`_pkgdown.yml`)
- Customize `_pkgdown.yml` as needed, then
- Run `pkgdown::build_site_github_pages()` to build your site
- Push the changes to deploy it online for others to access!
"
)
```
---
In this chapter, we'll cover setting up a `pkgdown` website for our app-package. Building a package website isn't required, but it's a great way to confirm your package is documented and structured correctly, and it gives you an opportunity to share all of your hard work! `pkgdown` can be configured to automatically generate a beautiful website from a pre-specified Git branch GitHub Actions.
:::: {.callout-tip collapse='true' appearance='default' icon=false}
## [Accessing the code examples]{style='font-weight: bold; font-size: 1.15em;'}
::: {style='font-size: 0.95em; color: #282b2d;'}
I've created the [`shinypak` R package](https://mjfrigaard.github.io/shinypak/) In an effort to make each section accessible and easy to follow:
Install `shinypak` using `pak` (or `remotes`):
```{r}
#| code-fold: false
#| message: false
#| warning: false
#| eval: false
# install.packages('pak')
pak::pak('mjfrigaard/shinypak')
```
Review the chapters in each section:
```{r}
#| code-fold: false
#| message: false
#| warning: false
#| collapse: true
library(shinypak)
list_apps(regex = 'pkgdown')
```
Launch an app:
```{r}
#| code-fold: false
#| eval: false
launch(app = "17_pkgdown")
```
:::
::::
A `pkgdown` website makes our Shiny app and its accompanying package more accessible to potential users by providing them with a central location for any information they need (app features, updates, etc.).
## Setting up [`pkgdown`]{style='font-size: 1.05em;'} {#sec-pkgdown-setup}
The magic of `pkgdown` is it's conversion of an existing R package structure into a website with documentation for our application.
``` r
install.packages("pkgdown")
```
`pkgdown` has a `usethis` function similar to `testthat` for setup:[^use-pkgdown-github-pages]
[^use-pkgdown-github-pages]: `usethis` also has a generic function for using `pkgdown` (`use_pkgdown()`), but we're going to cover building and deploying our app-package site using GitHub pages. Read more about `use_pkgdown()` in the [`usethis` documentation](https://usethis.r-lib.org/reference/use_pkgdown.html).
``` r
usethis::use_pkgdown_github_pages()
```
`use_pkgdown_github_pages()` takes care of (most of) the setup for our app-package website, but we'll break down the steps below.[^read-more-use-pkgdown-github_pages] I've replaced my GitHub username with `<username>` and the name of the app-package/repository with `<pkgName>`):
[^read-more-use-pkgdown-github_pages]: Internally, this function calls [`usethis::use_pkgdown()`](https://usethis.r-lib.org/reference/use_pkgdown.html), [`usethis::use_github_pages()`](https://usethis.r-lib.org/reference/use_github_pages.html), and [`usethis::use_github_action("pkgdown")`](https://usethis.r-lib.org/reference/use_github_action.html). Read more in the [`usethis` documentation.](https://usethis.r-lib.org/reference/index.html)
### [`_pkgdown.yml`]{style='font-size: 1.05em;'}
The initial output after running `use_pkgdown_github_pages()` looks something like the following:
``` sh
✔ Setting active project to '/Users/<username>/projects/<pkgName>'
✔ Adding '^_pkgdown\\.yml$', '^docs$', '^pkgdown$' to '.Rbuildignore'
✔ Adding 'docs' to '.gitignore'
✔ Writing '_pkgdown.yml'
• Modify '_pkgdown.yml'
✔ Recording 'https://<username>.github.io/<pkgName>/' as site's url in '_pkgdown.yml'
✔ Adding 'https://<username>.github.io/<pkgName>/' to URL
✔ Setting 'https://<username>.github.io/<pkgName>/' as homepage of GitHub repo '<username>/<pkgName>'
```
`_pkgdown.yml` is initially created with only the `url`, `template`, and `bootstrap` version:
``` yml
url: https://<username>.github.io/<pkgName>/
template:
bootstrap: 5
```
These fields are all that's required to launch your pkgdown site, but in the following sections we'll cover how to edit `_pkgdown.yml` to customize the fonts, colors, contents, and layout of our site.
### [`gh-pages`]{style='font-size: 1.05em;'} branch
`use_pkgdown_github_pages()` sets up publishing our app-package site from an '*orphan branch from GitHub pages*':
``` sh
✔ Initializing empty, orphan 'gh-pages' branch in GitHub repo '<username>/<pkgName>'
✔ GitHub Pages is publishing from:
• URL: 'https://<username>.github.io/<pkgName>/'
• Branch: 'gh-pages'
```
```{r}
#| label: co_box_orphan_branch
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "g",
look = "default", hsize = "1.10", size = "1.05",
header = "![](images/github.jpg){width='35' height='35'} Orphan branches",
fold = TRUE,
contents = "
An orphan branch is a new Git branch with no commit history, effectively starting a new '`root`' in our project's development history. For our app-package, the `gh-pages` branch serves as a new line of development, completely separated from all other branches.
"
)
```
We're also told GitHub pages will be publishing our app-package website at the following URL: `https://<username>.github.io/<pkgName>/`
### [`.github/workflows/`]{style='font-size: 1.05em;'}
`use_pkgdown_github_pages()` creates a GitHub Action workflow folder (`.github/workflows/`) with a YAML file (`pkgdown.yaml`):
``` sh
• Path: '/'
✔ Creating '.github/'
✔ Adding '^\\.github$' to '.Rbuildignore'
✔ Adding '*.html' to '.github/.gitignore'
✔ Creating '.github/workflows/'
✔ Saving 'r-lib/actions/examples/pkgdown.yaml@v2' to '.github/workflows/pkgdown.yaml'
• Learn more at <https://github.com/r-lib/actions/blob/v2/examples/README.md>.
```
We're also told the contents in this file are copied from the [r-lib/actions repository](https://github.com/r-lib/actions) (which we've covered previously in @sec-gha-style and @sec-gha-shiny).
## Building site {#sec-pkgdown-site-contents}
`usethis` has two functions for building your pkgdown site:
1. `build_site()`
2. `build_site_github_pages()`
We used `use_pkgdown_github_pages()` to configure our app-package, so we'll use `build_site_github_pages()` to build our site.
``` r
pkgdown::build_site_github_pages()
```
In the following sections, we'll take a look at how the files and folders in our app-package are used to create the site's contents. As mentioned above, the great thing about `pkgdown` sites is that they use our existing package structure to build a beautiful site that's easy to navigate (with minimal changes).
### `docs/`
The `docs/` folder contains the .html files for our website (that's why `^docs$` was added to the `.Rbuildignore` and `docs` was added to the `.gitignore`). After creating a home for our site contents, the site initialization files are copied from the local `pkgdown` installation into `docs/`
``` sh
== Building pkgdown site ======================================================
Reading from: '/Users/<username>/<pkgName>'
Writing to: '/Users/<username>/<pkgName>/docs'
```
### `README.md` -> `index.html`
The landing page (`index.html`) for our app-package website is built from the `README.md` file. An example of the site URL is below:
``` sh
https://<username>.github.io/<pkgName>/index.html
```
The `<username>` is our GitHub username, and the `<pkgName>` is the name of our package. The `authors.html` is built from the `Author` and `Maintainer` fields in the `DESCRIPTION` file. Long-form documentation can be stored in vignettes (@sec-use-vignette) which will be converted into articles (covered below).
```{r}
#| label: co_box_use_readme_rmd
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "g",
look = "default", hsize = "1.10", size = "1.05",
header = "Standard README for your app-package",
fold = TRUE,
contents =
"
If you'd like a boilerplate `README.md` for an R package, you can use `usethis::use_readme_rmd()`:
\`\`\` r
usethis::use_readme_rmd()
\`\`\`
\`\`\` bash
✔ Setting active project to '/Users/<username>/projects/<pkgName>'
✔ Writing 'README.Rmd'
✔ Adding '^README\\.Rmd$' to '.Rbuildignore'
• Modify 'README.Rmd'
✔ Writing '.git/hooks/pre-commit'
\`\`\`
At minimum, the `README.Rmd` should include:
1. The purpose/goal of your app-package
2. Instructions for installation
3. Links to a deployed version (if applicable)
4. Supporting packages
If I chose to use this `README.md` file, I usually remove the `.git/hooks/pre-commit` (so they don't interfere with my personal add/commit/push process).
\`\`\` r
unlink('.git/hooks/pre-commit')
\`\`\`
")
```
### `man/` -> [Reference](https://mjfrigaard.github.io/sap/reference/index.html)
The functions documented in the `man/` folder are converted into individual items in the **Reference** menu item (see @sec-doc). I've included two examples below:
:::: {layout="[50, 50]" layout-valign="top"}
``` sh
man/
├── display_type.Rd
└── ggp2_launch_app.Rd
```
``` sh
-- Building function reference ---------
Writing 'reference/index.html'
Reading 'man/display_type.Rd'
Writing 'reference/display_type.html'
Reading 'man/ggp2_launch_app.Rd'
Writing 'reference/ggp2_launch_app.html'
```
::::
- Functions will only be included in the **Reference** if they've been exported (see @sec-depends-exports)
- If we've been thorough in documenting, the `@seealso` and `@family` tags will create hyperlinks between our utility functions, modules, UI/server/standalone app functions (see @sec-document-app-functions)
- The `@examples` will be run and displayed (@sec-roxygen2-examples)
### `vignettes` -> [Articles](https://mjfrigaard.github.io/sap/articles/sap.html)
Any of the `.Rmd` in the `vignettes` folder will be rendered as HTML articles under the **Articles** menu item. The exception to this is any vignettes with the same name as our app-package (which will automatically be listed under a menu dropdown titled "Get Started"):[^get-started-vignette]
:::: {layout="[50, 50]" layout-valign="top"}
``` sh
vignettes/
├── sap.Rmd
└── specs.Rmd
```
``` sh
-- Building articles -------------
Writing 'articles/index.html'
Reading 'vignettes/sap.Rmd'
Writing 'articles/sap.html'
Reading 'vignettes/specs.Rmd'
Writing 'articles/specs.html'
== DONE ==========================
```
::::
[^get-started-vignette]: I created this vignette with `usethis::use_vignette("sap")` and included instructions for launching the various apps in `sap`.
The final step in the build process is to add a `.nojekyll` file in the repository (this hidden file is necessary for `pkgdown` sites configured to deploy from GitHub pages).
``` sh
-- Extra files for GitHub pages ----------------------------------------------
Writing '.nojekyll'
```
## Customize site layout {#sec-pkgdown-layouts}
We can customize the look of our `pkgdown` site by editing the contents of `_pkgdown.yml`.
### Themes, colors and fonts
Below are some examples of the fields that control the [bootswatch theme](https://bootswatch.com/) (`<THEME>`), [code syntax highlighting](https://pkgdown.r-lib.org/articles/customise.html#syntax-highlighting)(`<HIGHLIGHTING>`):
:::: {layout="[50, 50]" layout-valign="top"}
##### [YAML Fields]{style='font-weight: bold;'} {.unnumbered}
``` yml
template:
bootstrap: 5
bootswatch: <THEME>
theme: <HIGHLIGHTING>
```
##### [In `_pkgdown.yml`]{style='font-weight: bold;'} {.unnumbered}
``` yml
template:
bootstrap: 5
bootswatch: united
theme: atom-one-light
```
::::
We can use the [`bslib` package](https://rstudio.github.io/bslib/articles/any-project/index.html) for additional control over the fonts and colors on our site. The `<COLOR>` should be replaced with a color hex, and `<FONT>` can include any freely available [Google fonts](https://fonts.google.com/).[^read-more-bslib]
[^read-more-bslib]: Read more about `bslib` in `pkgdown` sites in the [documentation.](https://pkgdown.r-lib.org/articles/customise.html#bslib-variables)
:::: {layout="[50, 50]" layout-valign="top"}
##### [YAML Fields]{style='font-weight: bold;'} {.unnumbered}
``` yml
bslib:
primary: "<COLOR>"
code-color: "<COLOR>"
code-bg: "<COLOR>"
base_font:
google: <FONT>
heading_font:
google: <FONT>
code_font:
google: <FONT>
```
##### [In `_pkgdown.yml`]{style='font-weight: bold;'} {.unnumbered}
``` yml
bslib:
primary: "#007987"
secondary: "#f5feff"
base_font:
google: Ubuntu
heading_font:
google: Fira Sans Condensed
code_font:
google: Inconsolata
```
::::
You can see the theme, color, and font choices below:
![pkgdown `template` in yml](images/pkgdown_template_yml.png){width='90%'}
### Articles
The articles should include a link to the landing page for our app, and provide detailed examples of how it works, including links to any additional resources or documentation.
The `navbar` components can also be customized with titles, sections, and article names. In `_pkgdown.yml`, the `articles` are listed under `components`, and we will add a `text` title (`Docs`) and sub-heading (`Specs`):
:::: {layout="[50, 50]" layout-valign="top"}
##### [In `_pkgdown.yml`]{style='font-weight: bold;'} {.unnumbered}
``` yml
navbar:
components:
articles:
text: Docs
menu:
- text: "Specs"
```
##### [Output]{style='font-weight: bold;'} {.unnumbered}
![Navbar components](images/pkgdown_docs.png)
::::
Any vignette with a filename that matches the package name is automatically named 'Getting Started' in the navbar. We can also add sections with article titles by placing them in `text` fields. These are listed under the `menu` (note the indentation), with a path to the `html` file.
- Below I've listed the `App Specifications` vignette under a `"Specs"` section (see @sec-tests-specs), and linked to `articles/specs.html`:
:::: {layout="[50, 50]" layout-valign="top"}
##### [In `_pkgdown.yml`]{style='font-weight: bold;'} {.unnumbered}
``` yml
text: Docs
menu:
- text: "Specs"
- text: App Specifications
href: articles/specs.html
```
##### [Output]{style='font-weight: bold;'} {.unnumbered}
![Article sections](images/pkgdown_docs_menu.png)
::::
Use `-------` with a `text` field to create horizontal separators between sections (without a corresponding `href`).
- I've added a `Features` section and a `Application Features` vignette (stored in `vignettes/features.Rmd` and published to `articles/features.html`):
:::: {layout="[50, 50]" layout-valign="top"}
##### [In `_pkgdown.yml`]{style='font-weight: bold;'} {.unnumbered}
``` yml
text: Docs
menu:
- text: "Specs"
- text: App Specifications
href: articles/specs.html
- text: -------
- text: "Features"
- text: App Features
href: articles/features.html
```
##### [Output]{style='font-weight: bold;'} {.unnumbered}
![Articles separator](images/pkgdown_docs_menu_separator.png)
::::
### Function reference {#sec-pkgdown-reference}
`pkgdown` will automatically generate a **Package index** section for any object with an `.Rd` file in the `man/` folder. This includes functions we've explicitly exported with (i.e., with `@export`) *and* functions we've documented with `@keywords internal`.[^exported-funs]
[^exported-funs]: Functions with `@keywords internal` aren't listed in the package index, but can be accessed with `pkg:::fun()` (like the `test_logger()` function in `sap`).
By default, the function are sorted alphabetically, but we can customize them into sections with titles and descriptions using the fields below in `_pkgdown.yml`:
``` yml
reference:
- title: "<TITLE>"
desc: >
<DESCRIPTION>
contents:
- <FUNCTION>
```
For example, we can include a section for the modules we're exporting from our app-package:
:::: {layout="[50, 50]" layout-valign="top"}
##### [In `_pkgdown.yml`]{style='font-weight: bold;'} {.unnumbered}
``` yml
reference:
- title: "Modules"
desc: >
App modules
```
##### [Output]{style='font-weight: bold;'} {.unnumbered}
![](images/pkgdown_fun_title_desc_mods.png){width='100%' fig-align='center'}
::::
```{r}
#| label: co_box_list_all_funs_reference
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "o",
look = "default", hsize = "1.10", size = "1.05",
header = "Customizing **`Reference`**",
fold = TRUE,
contents =
"
The `_pkgdown.yml` file **must include all exported functions if you customize the `reference` field**. If not, you'll see an error when you try to build your site:
\`\`\` bash
-- Building function reference ------------------------------------
Error in `check_missing_topics()`:
! All topics must be included in reference index
✖ Missing topics: <FUN>
ℹ Either add to _pkgdown.yml or use @keywords internal
\`\`\`
")
```
To help organize and display the functions in your app-package, we can use `tidyselect` helpers[^tidyselect] in the bullets below `contents`.
For example, we can list modules with `starts_with("mod")`:[^prefix-names]
[^prefix-names]: Using the `mod_` as a prefix for module functions is a habit I've adopted from the [`golem` package](https://thinkr-open.github.io/golem/) (specifically, the [`add_module()` function](https://thinkr-open.github.io/golem/reference/add_module.html)).
[^tidyselect]: Read more about how to build the function reference [here](https://pkgdown.r-lib.org/articles/pkgdown.html#reference)
:::: {layout="[50, 50]" layout-valign="top"}
##### [In `_pkgdown.yml`]{style='font-weight: bold;'} {.unnumbered}
``` yml
reference:
- title: "Modules"
desc: >
Application modules
contents:
- starts_with("mod")
```
##### [Output]{style='font-weight: bold;'} {.unnumbered}
![](images/pkgdown_fun_ref_mods.png){width='100%' fig-align='center'}
::::
We can also use `_pkgdown.yml` to list any datasets we've documented (see @sec-document-data) in our app-package.
:::: {layout="[50, 50]" layout-valign="top"}
##### [In `_pkgdown.yml`]{style='font-weight: bold;'} {.unnumbered}
``` yml
- title: "Data"
desc: "App data"
contents:
- movies
```
##### [Output]{style='font-weight: bold;'} {.unnumbered}
![](images/pkgdown_fun_ref_data.png){width='100%' fig-align='center'}
::::
## Deploying your site {#sec-pkgdown-workflow}
The `.github/workflows/pkgdown.yaml` file automates building and deploying our app-package's `pkgdown` site. This workflow file is configured to be triggered by specific GitHub events, build the website using the standard package files, then deploys it to GitHub Pages. Below we'll breakdown the fields and values of the workflow (and their functions):
### Triggers
::::{layout="[50, 50]" layout-valign="center"}
[1. Set to trigger **`on`** `push`es or `pull_request`s made to the `main` or `master` branches (__we'll change these to only trigger on the `17_pkgdown` branch__).]{style="font-size: 0.90rem;"}
``` yml
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
```
::::
::::{layout="[-3, 47, 50]" layout-valign="center"}
[a. Also triggers when a `release` is published, allowing the website to showcase the latest version of the package.[^releases]]{style="font-size: 0.90rem;"}
[^releases]: [*"You can create releases to bundle and deliver iterations of a project to users."*](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release)
``` yml
release:
types: [published]
```
::::
::::{layout="[-3, 47, 50]" layout-valign="center"}
[b. `workflow_dispatch` allows the workflow to be manually triggered from the GitHub Actions web interface (for ad-hoc updates).[^manual-runs]]{style="font-size: 0.90rem;"}
[^manual-runs]: [*"To enable a workflow to be triggered manually, you need to configure the `workflow_dispatch` event."*](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch)
``` yml
workflow_dispatch:
```
::::
### Jobs
::::{layout="[50, 50]" layout-valign="center"}
[2. **`name`** defines a single job with the ID `pkgdown`.]{style="font-size: 0.90rem;"}
``` yml
name: pkgdown
```
::::
::::{layout="[50, 50]" layout-valign="center"}
[3. **`jobs`** specifies the job ID (`pkgdown`) and runs the job on the latest Ubuntu runner provided by GitHub Actions.]{style="font-size: 0.90rem;"}
``` yml
jobs:
pkgdown:
runs-on: ubuntu-latest
```
::::
[4. The comment *`# Only restrict concurrency for non-PR jobs`* refers to the **`concurrency`** field,[^concurrency] which prevents *concurrent* runs of the job for non-pull request events (avoiding conflicts or redundant deployments).]{style="font-size: 0.90rem;"}
[a. `group`[^group] uses a dynamic expression to differentiate between `pull_request` events and `github.event_name`, using the run ID (`github.run_id`) for pull requests to allow concurrency.]{style="font-size: 0.90rem;"}
[^concurrency]: [*"Use concurrency to ensure that only a single job or workflow using the same concurrency group will run at a time."*](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency)
[^group]: [*"Concurrency groups provide a way to manage and limit the execution of workflow runs or jobs that share the same concurrency key."*](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-concurrency-groups)
``` yml
concurrency:
group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
```
[5. **`env`** sets the `GITHUB_PAT` environment variable using the GitHub token, `secrets.GITHUB_TOKEN` (which allows the workflow to authenticate and perform operations within the repository).]{style="font-size: 0.90rem;"}
``` yml
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
```
:::: {layout="[50, 50]" layout-valign="center"}
[6. **`permissions`** explicitly grants the workflow `write` permissions to the repository, enabling it to push changes (like an updated website).]{style="font-size: 0.90rem;"}
``` yml
permissions:
contents: write
```
::::
### Steps
:::: {layout="[50, 50]" layout-valign="center"}
[7. Checks out the repository's code, making it available to subsequent **`steps`**.]{style="font-size: 0.90rem;"}
``` yml
steps:
- uses: actions/checkout@v4
```
::::
:::: {layout="[50, 50]" layout-valign="center"}
[8. Installs **`pandoc`**, which is necessary for rendering markdown documents and vignettes.]{style="font-size: 0.90rem;"}
``` yml
- uses: r-lib/actions/setup-pandoc@v2
```
::::
:::: {layout="[50, 50]" layout-valign="center"}
[9. Sets up the R environment and configures it to use the public RStudio package manager (**`use-public-rspm`**) for faster package installations.]{style="font-size: 0.90rem;"}
``` yml
- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
```
::::
:::: {layout="[50, 50]" layout-valign="center"}
[10. Installs the **`dependencies`** required by our app-package and `pkgdown`.]{style="font-size: 0.90rem;"}
``` yml
- uses: r-lib/actions/setup-r-dependencies@v2
```
::::
:::: {layout="[-3, 47, 50]" layout-valign="center"}
[a. Specifies installing **`any::`**thing from `pkgdown` and the **`local::`** package (our app-package).[^needs]]{style="font-size: 0.90rem;"}
[^needs]: The **`needs: website`** fields might be a placeholder? I'm unaware of the **`needs`** keyword applied within a **`with`** clause for setting up dependencies. This also could be intended as a comment or note for future adjustments...
``` yml
with:
extra-packages: any::pkgdown, local::.
needs: website
```
::::
[11. Executes `pkgdown::build_site_github_pages()` within an R script shell to build the **`pkgdown`** website. It's configured not to start a new R process for the build and not to install our app-package (assuming dependencies are already handled in step 10).]{style="font-size: 0.90rem;"}
``` yml
- name: Build site
run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
shell: Rscript {0}
```
[12. Uses **`JamesIves/[email protected]`** to deploy the site to GitHub Pages (provided the event is not a pull request).]{style="font-size: 0.90rem;"}
``` yml
- name: Deploy to GitHub pages 🚀
if: github.event_name != 'pull_request'
uses: JamesIves/[email protected]
```
:::: {layout="[-3, 47, 50]" layout-valign="center"}
[a. Specifies not to **`clean`** the deployment branch, deploys it to the **`gh-pages`** branch, and sets the site content source **`folder`** to **`docs`**]{style="font-size: 0.90rem;"}
``` yml
with:
clean: false
branch: gh-pages
folder: docs
```
::::
`.github/workflows/pkgdown.yaml` performs more operations than the previous workflows. However, these extra steps allow us to use the `gh-pages` branch to maintain and showcase up-to-date package documentation for users, contributors, and collaborators. When we're happy with the layout of our website in `_pkgdown.yml`, we can add, commit, and push the changes back to the repo:
``` sh
git add .
git commit -m "updates to _pkgdown.yml"
git push
```
## GitHub {#sec-pkgdown-github}
Back in GitHub, we can see our previous **Style** (@sec-gha-style), **shiny** (@sec-gha-shiny), and **docker-shiny, moviesapp** (@sec-gha-shiny-docker) workflows:
![](images/pkgdown_gha_all_workflows.png){width='100%'}
```{r}
#| label: git_box_17_pkgdown
#| echo: false
#| results: asis
#| eval: true
git_margin_box(
contents = "launch",
fig_pw = '75%',
branch = "17_pkgdown",
repo = 'sap')
```
After pushing the changes to the `17_pkgdown` branch, a new workflow is created and run in the **Actions** tab:
![Workflow created on `push` to `17_pkgdown` branch](images/pkgdown_new_workflow.png)
The `workflow` file triggers the following steps:
![GitHub Pages Deploy Action](images/pkgdown_gh_deploy_pkgdown.png){width='100%'}
- The process begins with checking the necessary configurations for deployment and using the `Deploy Token… 🔑` for authentication.
``` sh
Checking configuration and starting deployment… 🚦
Deploying using Deploy Token… 🔑
```
- Git is set up and configured (similar to a new user) with user credentials (`<username>` and `<username>@pm.me`), but also includes:
- setting up a safe directory (`--add safe.directory`),
- ignoring case (`config core.ignorecase false`),
- removing any extra headers (`http.https://github.com/.extraheader`), and
- adding remote origin to point to the GitHub repository where the `sap` package resides (`remote add origin`).
``` sh
Configuring git…
/usr/bin/git config --global --add safe.directory /home/runner/work/<pkgName>/<pkgName>
/usr/bin/git config user.name <username>
/usr/bin/git config user.email <username>@pm.me
/usr/bin/git config core.ignorecase false
/usr/bin/git config --local --unset-all http.https://github.com/.extraheader
/usr/bin/git remote rm origin
/usr/bin/git remote add origin ***github.com/<username>/<pkgName>.git
Git configured… 🔧
```
- The workflow checks for an existing `gh-pages` branch in the repository (which is the branch we've configured to serve the website content).
``` sh
Starting to commit changes…
/usr/bin/git ls-remote --heads ***github.com/<username>/<pkgName>.git refs/heads/gh-pages
7cac1013e4a324d943d2b33ed6d52f0cf6b243a6 refs/heads/gh-pages
```
- A Git `worktree`[^worktree] is created for the `gh-pages` branch, which allows multiple branches within the same repository (without having to clone the repository again).
- The worktree is prepared in a `detached HEAD` state, and then the `gh-pages` branch is checked out:
``` sh
Creating worktree…
/usr/bin/git fetch --no-recurse-submodules --depth=1 origin gh-pages
From https://github.com/<username>/<pkgName>
* branch gh-pages -> FETCH_HEAD
* [new branch] gh-pages -> origin/gh-pages
/usr/bin/git worktree add --no-checkout --detach github-pages-deploy-action-temp-deployment-folder
Preparing worktree (detached HEAD 742bb18)
/usr/bin/git checkout -B gh-pages origin/gh-pages
```
- The website content generated by `pkgdown` in the `docs` directory is copied into the worktree directory, ensuring that all necessary files are updated with the latest changes:
- `chmod` is used to 'change access permissions'[^chmod]
- `-R` applies the permission changes to the directory specified *and* to all of the subdirectories and files
- `+rw` adds both read and write permissions for the user, group, and others to the files/directories targeted by this command
``` sh
/usr/bin/chmod -R +rw /home/runner/work/<pkgName>/<pkgName>/docs
/usr/bin/rsync -q -av --checksum --progress /home/runner/work/<pkgName>/<pkgName>/docs/. github-pages-deploy-action-temp-deployment-folder --exclude .ssh --exclude .git --exclude .github
```
- The changes are added, committed, and then force-pushed to the `gh-pages` branch:
- The content is updated to reflect the latest version of the website associated the `sap` R package
- The commit message includes a deployment tag and the hash of the commit from the deployment
``` sh
Checking if there are files to commit…
/usr/bin/git add --all .
/usr/bin/git checkout -b github-pages-deploy-action/kpvxdgnej
Switched to a new branch 'github-pages-deploy-action/kpvxdgnej'
Force-pushing changes...
/usr/bin/git push --force ***github.com/<username>/<pkgName>.git github-pages-deploy-action/kpvxdgnej:gh-pages
To https://github.com/<username>/<pkgName>.git
7cac101..aab5105 github-pages-deploy-action/kpvxdgnej -> gh-pages
Changes committed to the gh-pages branch… 📦
```
- After successfully pushing the changes to the `gh-pages` branch, the workflow performs `cleanup jobs`:
- reset the branch pointer, and
- remove the temporary worktree directory
``` sh
Running post deployment cleanup jobs… 🗑️
/usr/bin/git checkout -B github-pages-deploy-action/kpvxdgnej
Reset branch 'github-pages-deploy-action/kpvxdgnej'
/usr/bin/chmod -R +rw github-pages-deploy-action-temp-deployment-folder
/usr/bin/git worktree remove github-pages-deploy-action-temp-deployment-folder --force
```
- The workflow finishes and signals the deployment has been successfully completed.
``` sh
Completed deployment successfully! ✅
```
Back in the **Actions** tab, we see a new **pages build and deployment** workflow has been created with a 'bot' tag:
![](images/pkgdown_pages_build_deployment_workflow.png){width='100%'}
The **updates to \_pkgdown.yml** workflow represents the changes we committed and pushed to the `17_pkgdown` branch (`#10`), but we configured our website to be served from the `gh-pages` branch (`#11`). Each time we push changes to `17_pkgdown` and trigger the **pkgdown** workflow, a corresponding **pages build and deployment** workflow will be triggered to build and deploy the site:
![Build and deploy `pkgdown` site](images/pkgdown_build_deploy_report.png)
This automated deployment process is essential for maintaining up-to-date documentation or website content for R packages (like `sap`) without manual intervention, making it easier for developers to focus on development while ensuring that users always have access to the latest information.
View the package website [here.](https://mjfrigaard.github.io/sap/)
[^worktree]: [*"A git repository can support multiple working trees, allowing you to check out more than one branch at a time."*](https://git-scm.com/docs/git-worktree)
[^chmod]: [*"`chmod` changes the permissions of each given file according to mode, where mode describes the permissions to modify."*](https://ss64.com/bash/chmod.html)
## Recap {.unnumbered}
```{r}
#| label: co_box_recap
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "g",
fold = FALSE,
look = "default", hsize = "1.10", size = "1.05",
header = "Recap: ![](images/pkgdown.png){width='50' height='55'}",
contents = "
<br>
**`pkgdown`** is handy for creating beautiful, functional websites for your app-package. Package sites help share your app-package with others in a more engaging and informative way.
- **Installation**
\`\`\` r
install.packages('pkgdown)`
\`\`\`
- **Setup**: create a configuration file that `pkgdown` will use to build your site with one of the `usethis` functions below:
\`\`\` r
usethis::use_pkgdown_github_pages()
# or
# usethis::use_pkgdown()
\`\`\`
- These create a `_pkgdown.yml` configuration file that lets us customize how our site looks and which parts of our app-package are diplayed/highlighted.
- **Customize**: we can change our `pkgdown` site theme, set colors and fonts, organize the navigation bar, and add custom sections and pages. For an app-package, this means we can create a landing page for our Shiny app, provide detailed articles on how it works, and link to any additional resources or documentation.
- **Building Your Site**
\`\`\` r
pkgdown::build_site_github_pages()
# or
# pkgdown::build_site()
\`\`\`
- `build_site_github_pages()` goes through our app-package (function documentation, and examples, RMarkdown vignettes, README, NEWS, etc.) and assembles a coherent, navigable site.
- **Deploying Your Site**: after building a `pkgdown` site, we can use GitHub Actions to upload the generated website contents to GitHub Pages, which hosts our app-package site directly from a GitHub repository.
")
```