-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathexamples.R
More file actions
143 lines (131 loc) · 4.46 KB
/
Copy pathexamples.R
File metadata and controls
143 lines (131 loc) · 4.46 KB
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
#!/usr/bin/env Rscript
# Keep site/examples in sync with the repos its dictionaries come from.
#
# Rscript examples.R sync validate each repo, then update site/examples/<name>.yaml
# Rscript examples.R render render each dictionary to site/examples/rendered/<name>.html
#
# Both modes clone the whole source repo: the tables' source data has to be
# there to validate against and to profile into the rendered page. Run from the
# repo root, with the CLI built (`cargo build -p data-dict-cli --release`) or
# `DATA_DICT` pointing at a binary.
# `sync` writes a site/examples/<name>.qmd for each entry; dropping an entry
# leaves its .qmd and .yaml behind, to be deleted by hand.
repos <- c(
contoso = "hadley/contoso",
dabstep = "hadley/dabstep",
elevators = "hadley/elevators",
foodbank = "hadley/foodbank",
`loan-application` = "hadley/loan-application",
otters = "hadley/otters"
)
data_dict <- function(...) {
bin <- Sys.getenv("DATA_DICT", "target/release/data-dict")
system2(bin, c(...)) == 0
}
clone <- function(repo, dest) {
url <- paste0("https://github.com/", repo, ".git")
status <- system2("git", c("clone", "--quiet", "--depth", "1", url, dest))
if (status != 0) {
stop("failed to clone ", repo, call. = FALSE)
}
}
# `::group::` folds each example's output in the workflow log.
group <- function(name, expr) {
cat("::group::", name, "\n", sep = "")
on.exit(cat("::endgroup::\n"))
expr
}
yaml_path <- function(name) file.path("site", "examples", paste0(name, ".yaml"))
# The wrapper page each dictionary gets on the site. `page` and `raw` are the
# link columns of the listing in site/examples/index.qmd.
write_qmd <- function(name, repo) {
writeLines(
c(
"---",
sprintf('title: "%s"', name),
sprintf('description: "Synced from %s"', repo),
sprintf('page: "[Browse](rendered/%s.html)"', name),
sprintf('raw: "[Download](%s.yaml)"', name),
"---",
"",
"<!-- Generated by examples.R; edit that instead. -->",
"",
sprintf(
"A real-world `data-dict.yaml`, synced from [%s](https://github.com/%s).",
repo,
repo
),
sprintf(
"[Browse the rendered dictionary](rendered/%s.html), or [download the raw file](%s.yaml).",
name,
name
),
"",
sprintf('```{.yaml filename="%s.yaml"}', name),
sprintf("{{< include %s.yaml >}}", name),
"```"
),
file.path("site", "examples", paste0(name, ".qmd"))
)
}
sync <- function(name, repo, src) {
dict <- file.path(src, "data-dict.yaml")
# validate-data implies the spec and metadata levels.
ok <- data_dict("validate-data", dict)
if (ok) {
source_url <- paste0("https://github.com/", repo)
writeLines(c(paste0("# source: ", source_url), "", readLines(dict)), yaml_path(name))
} else {
cat(sprintf(
"::error title=%s::validate-data failed; keeping the copy already in site/examples\n",
name
))
}
# A page that included a yaml that isn't there would fail the site build.
if (file.exists(yaml_path(name))) {
write_qmd(name, repo)
}
ok
}
render <- function(name, repo, src) {
# Render the copy in site/examples: that is the one the site shows, and it is
# the one `sync` has validated.
dict <- file.path(src, "data-dict.yaml")
file.copy(yaml_path(name), dict, overwrite = TRUE)
out <- file.path("site", "examples", "rendered", paste0(name, ".html"))
if (!data_dict("render", dict, "--output", out)) {
cat(sprintf(
"::warning title=%s::could not be rendered; the site will ship without its page\n",
name
))
return(FALSE)
}
TRUE
}
mode <- commandArgs(trailingOnly = TRUE)
if (length(mode) != 1 || !mode %in% c("sync", "render")) {
stop("usage: Rscript examples.R [sync|render]", call. = FALSE)
}
if (mode == "render") {
dir.create(file.path("site", "examples", "rendered"), showWarnings = FALSE)
}
checkouts <- tempfile("examples-")
dir.create(checkouts)
ok <- logical(length(repos))
for (i in seq_along(repos)) {
name <- names(repos)[[i]]
ok[[i]] <- group(name, {
src <- file.path(checkouts, name)
clone(repos[[i]], src)
switch(mode, sync = sync, render = render)(name, repos[[i]], src)
})
}
# A failed render still deploys, without that page; a failed validation fails
# the run, but only after every other example has been synced.
if (mode == "sync" && !all(ok)) {
cat(sprintf(
"::error::Examples failing validate-data: %s\n",
paste(names(repos)[!ok], collapse = ", ")
))
quit(status = 1)
}