Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/publish/config.ts
6442 views
1
/*
2
* config.ts
3
*
4
* Copyright (C) 2022 Posit Software, PBC
5
*/
6
7
import { warning } from "../deno_ral/log.ts";
8
import { stringify } from "../core/yaml.ts";
9
import { basename, dirname, join } from "../deno_ral/path.ts";
10
11
import { Metadata } from "../config/types.ts";
12
import { readYaml, readYamlFromString } from "../core/yaml.ts";
13
import { ProjectContext } from "../project/types.ts";
14
import { PublishDeployments, PublishRecord } from "./types.ts";
15
import { AccountToken } from "./provider-types.ts";
16
import { writePublishRecord } from "./common/data.ts";
17
import { existsSync1 } from "../core/file.ts";
18
19
export function readPublishDeployments(
20
source: string,
21
): PublishDeployments {
22
const [deployDir, deploySource] = resolveDeploymentSource(source);
23
const deplomentsFile = publishDeploymentsFile(deployDir);
24
if (deplomentsFile) {
25
const deployments = readYaml(deplomentsFile);
26
if (deployments) {
27
if (isDeploymentsArray(deployments)) {
28
const sourceDeployments = deployments.find((deployment) =>
29
deployment.source === basename(deploySource)
30
);
31
if (sourceDeployments) {
32
delete (sourceDeployments as { source?: string }).source;
33
// provide 'code' field
34
Object.values(sourceDeployments).forEach((deployment) => {
35
if (Array.isArray(deployment)) {
36
deployment = deployment.map((d) => {
37
d.code = !!d.code;
38
return d;
39
});
40
}
41
});
42
return {
43
dir: deployDir,
44
source: deploySource,
45
records: sourceDeployments as Record<string, Array<PublishRecord>>,
46
};
47
}
48
} else {
49
warning(
50
"Unexpcted format for _publish.yml file (not reading publish history)",
51
);
52
}
53
}
54
}
55
56
return {
57
dir: deployDir,
58
source: deploySource,
59
records: {},
60
} as PublishDeployments;
61
}
62
63
export function writePublishDeployment(
64
source: string,
65
provider: string,
66
account: AccountToken,
67
publish: PublishRecord,
68
) {
69
// don't write 'code' field if false
70
publish = {
71
...publish,
72
} as PublishRecord;
73
if (publish.code === false) {
74
delete (publish as Record<string, unknown>).code;
75
}
76
77
// read base config
78
let indent = 2;
79
const [deployDir, deploySource] = resolveDeploymentSource(source);
80
const deploymentsFile = publishDeploymentsFile(deployDir);
81
if (deploymentsFile) {
82
const deploymentsFileYaml = Deno.readTextFileSync(deploymentsFile);
83
indent = detectIndentLevel(deploymentsFileYaml);
84
const deployments = readYamlFromString(deploymentsFileYaml);
85
if (isDeploymentsArray(deployments)) {
86
const docDeploymentsIdx = deployments.findIndex((deployment) =>
87
deployment.source === basename(deploySource)
88
);
89
if (docDeploymentsIdx !== -1) {
90
const docDeployments = deployments[docDeploymentsIdx];
91
if (docDeployments[provider]) {
92
const deploymentIdx = (docDeployments[provider] as PublishRecord[])
93
.findIndex(
94
(published) => published.id === publish.id,
95
);
96
if (deploymentIdx !== -1) {
97
(docDeployments[provider] as PublishRecord[])[deploymentIdx] =
98
publish;
99
} else {
100
(docDeployments[provider] as PublishRecord[]).push(publish);
101
}
102
} else {
103
docDeployments[provider] = [publish];
104
}
105
} else {
106
const deployment = {
107
source: deploySource,
108
[provider]: [publish],
109
};
110
deployments.push(deployment);
111
}
112
113
Deno.writeTextFileSync(
114
deploymentsFile,
115
stringifyPublishConfig(deployments, indent),
116
);
117
} else {
118
warning(
119
"Unexpcted format for _publish.yml file (not writing to publish history)",
120
);
121
}
122
} else {
123
Deno.writeTextFileSync(
124
join(deployDir, kDefaultPublishDeploymentsFile),
125
stringifyPublishConfig([{
126
source: deploySource,
127
[provider]: [publish],
128
}], indent),
129
);
130
}
131
132
// write a record of which account was was used to publish
133
// in a sidecar list so that we can pair it for republish
134
writePublishRecord(source, provider, account, publish);
135
}
136
137
function resolveDeploymentSource(source: string) {
138
const deployDir = Deno.statSync(source).isDirectory
139
? source
140
: dirname(source);
141
const deploySource = Deno.statSync(source).isDirectory
142
? "project"
143
: basename(source);
144
return [deployDir, deploySource];
145
}
146
147
export function readProjectPublishDeployments(
148
project: ProjectContext,
149
): PublishDeployments {
150
return readPublishDeployments(project.dir);
151
}
152
153
export function writeProjectPublishDeployment(
154
project: ProjectContext,
155
provider: string,
156
account: AccountToken,
157
publish: PublishRecord,
158
) {
159
writePublishDeployment(project.dir, provider, account, publish);
160
}
161
162
function isDeploymentsArray(
163
obj: unknown,
164
): obj is Array<Record<string, string | PublishRecord[]>> {
165
return Array.isArray(obj) && typeof (obj[0].source) === "string";
166
}
167
168
function stringifyPublishConfig(config: unknown, indent: number) {
169
return stringify(
170
config as Metadata,
171
{
172
indent,
173
lineWidth: -1,
174
sortKeys: false,
175
skipInvalid: true,
176
},
177
);
178
}
179
180
function detectIndentLevel(yaml: string) {
181
const spaceMatch = yaml.match(/\n(\s+)/);
182
return spaceMatch ? spaceMatch[1].length : 2;
183
}
184
185
const kDefaultPublishDeploymentsFile = "_publish.yml";
186
187
export function publishDeploymentsFile(dir: string): string | undefined {
188
return [kDefaultPublishDeploymentsFile, "_publish.yaml"]
189
.map((file) => join(dir, file))
190
.find(existsSync1);
191
}
192
193