Skip to content

Commit bf70ee6

Browse files
committed
bundle - add missing files for RPM build
1 parent 312db36 commit bf70ee6

4 files changed

Lines changed: 218 additions & 87 deletions

File tree

package/scripts/linux/rpm/postinst

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
# detect whether running as root (per machine installation)
5+
# if per machine (run without sudo):
6+
7+
if [[ $EUID -eq 0 ]]; then
8+
if [ -d "/usr/local/bin" ]
9+
then
10+
ln -fs /opt/quarto/bin/quarto /usr/local/bin/quarto
11+
else
12+
echo "Quarto symlink not created, please be sure that you add Quarto to your path."
13+
fi
14+
15+
if [ -d "/usr/local/man/man1" ]
16+
then
17+
ln -fs /opt/quarto/share/man/quarto.man /usr/local/man/man1/quarto.1
18+
elif [ -d "/usr/local/man" ]
19+
then
20+
ln -fs /opt/quarto/share/man/quarto.man /usr/local/man/quarto.1
21+
fi
22+
23+
else
24+
if [ -d "~/bin/quarto" ]
25+
then
26+
ln -fs /opt/quarto/bin/quarto ~/bin/quarto
27+
else
28+
echo "Quarto symlink not created, please be sure that you add Quarto to your path."
29+
fi
30+
31+
if [ -d "~/man/man1" ]
32+
then
33+
ln -fs /opt/quarto/share/man/quarto.man ~/man/man1/quarto.1
34+
elif [ -d "~/man" ]
35+
then
36+
ln -fs /opt/quarto/share/man/quarto.man ~/man/quarto.1
37+
fi
38+
39+
fi
40+
41+
# Figure architecture
42+
NIXARCH=$(uname -m)
43+
if [[ $NIXARCH == "aarch64" ]]; then
44+
ARCH_DIR=aarch64
45+
else
46+
ARCH_DIR=x86_64
47+
fi
48+
49+
ln -fs /opt/quarto/bin/tools/${ARCH_DIR}/pandoc /opt/quarto/bin/tools/pandoc
50+
51+
exit 0

package/scripts/linux/rpm/postrm

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
if [[ "$EUID" -eq 0 ]]
5+
then
6+
rm -f /usr/local/bin/quarto
7+
else
8+
rm -f ~/bin/quarto
9+
fi
10+
11+
# Remove pandoc symlink created by postinst
12+
# (before 1.4 this was a regular file that shouldn't be removed here)
13+
pandoc=/opt/quarto/bin/tools/pandoc
14+
if [ -h "$pandoc" ]
15+
then
16+
rm -f "$pandoc"
17+
fi
18+
19+
exit 0

package/src/bld.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { mainRunner } from "../../src/core/main.ts";
1010

1111
import { prepareDist } from "./common/prepare-dist.ts";
1212
import { updateHtmlDependencies } from "./common/update-html-dependencies.ts";
13-
import { makeInstallerDeb } from "./linux/installer.ts";
13+
import { makeInstallerDeb, makeInstallerRpm } from "./linux/installer.ts";
1414
import { makeInstallerMac } from "./macos/installer.ts";
1515
import {
1616
compileQuartoLatexmkCommand,
@@ -96,6 +96,10 @@ function getCommands() {
9696
packageCommand(makeInstallerDeb, "make-installer-deb")
9797
.description("Builds Linux deb installer"),
9898
);
99+
commands.push(
100+
packageCommand(makeInstallerRpm, "make-installer-rpm")
101+
.description("Builds Linux rpm installer"),
102+
);
99103
commands.push(
100104
packageCommand(makeInstallerWindows, "make-installer-win")
101105
.description("Builds Windows installer"),

package/src/linux/installer.ts

Lines changed: 143 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,120 @@
55
*
66
*/
77
import { join } from "../../../src/deno_ral/path.ts";
8-
import { copySync, emptyDirSync, ensureDirSync, walk } from "../../../src/deno_ral/fs.ts";
8+
import { copySync, emptyDirSync, ensureDirSync, existsSync, walk } from "../../../src/deno_ral/fs.ts";
99
import { info } from "../../../src/deno_ral/log.ts";
10+
import * as yaml from "../../../src/core/lib/external/js-yaml.js";
1011

1112
import { Configuration } from "../common/config.ts";
1213
import { runCmd } from "../util/cmd.ts";
1314

14-
export async function makeInstallerDeb(
15+
// Map architecture names between Quarto and package formats
16+
function mapArchitecture(arch: string, format: 'deb' | 'rpm'): string {
17+
if (format === 'deb') {
18+
return arch === 'x86_64' ? 'amd64' : 'arm64';
19+
} else { // rpm
20+
return arch === 'x86_64' ? 'x86_64' : 'aarch64';
21+
}
22+
}
23+
24+
// Create nfpm configuration for DEB or RPM packages
25+
async function createNfpmConfig(
26+
configuration: Configuration,
27+
format: 'deb' | 'rpm',
28+
workingDir: string,
29+
) {
30+
const arch = mapArchitecture(configuration.arch, format);
31+
const workingBinPath = join(
32+
workingDir,
33+
"opt",
34+
configuration.productName.toLowerCase(),
35+
"bin",
36+
);
37+
const workingSharePath = join(
38+
workingDir,
39+
"opt",
40+
configuration.productName.toLowerCase(),
41+
"share",
42+
);
43+
44+
const contents: any[] = [
45+
{
46+
src: workingBinPath,
47+
dst: "/opt/quarto/bin",
48+
type: "tree",
49+
},
50+
{
51+
src: workingSharePath,
52+
dst: "/opt/quarto/share",
53+
type: "tree",
54+
},
55+
];
56+
57+
// Add copyright file for DEB packages
58+
if (format === 'deb') {
59+
const copyrightFile = join(
60+
workingDir,
61+
"usr",
62+
"share",
63+
"doc",
64+
configuration.productName.toLowerCase(),
65+
"copyright",
66+
);
67+
contents.push({
68+
src: copyrightFile,
69+
dst: `/usr/share/doc/${configuration.productName.toLowerCase()}/copyright`,
70+
});
71+
}
72+
73+
const config: any = {
74+
name: configuration.productName.toLowerCase(),
75+
version: configuration.version,
76+
arch: arch,
77+
maintainer: "Posit, PBC <[email protected]>",
78+
description: "Quarto is an academic, scientific, and technical publishing system built on Pandoc.",
79+
homepage: "https://github.com/quarto-dev/quarto-cli",
80+
license: "MIT",
81+
82+
contents: contents,
83+
84+
scripts: {
85+
postinstall: join(configuration.directoryInfo.pkg, "scripts", "linux", format, "postinst"),
86+
postremove: join(configuration.directoryInfo.pkg, "scripts", "linux", format, "postrm"),
87+
},
88+
89+
overrides: {},
90+
};
91+
92+
// Format-specific configuration
93+
if (format === 'deb') {
94+
config.overrides.deb = {
95+
recommends: ["unzip"],
96+
};
97+
// Add Debian-specific metadata
98+
config.section = "user/text";
99+
config.priority = "optional";
100+
}
101+
return config;
102+
}
103+
104+
// Build package using nfpm
105+
async function buildPackageWithNfpm(
15106
configuration: Configuration,
107+
format: 'deb' | 'rpm',
16108
) {
17-
info("Building deb package...");
18-
19-
// detect packaging machine architecture
20-
// See complete list dpkg-architecture -L.
21-
// arm64
22-
// amd64
23-
const architecture = configuration.arch === "x86_64" ? "amd64" : "arm64";
24-
const packageName =
25-
`quarto-${configuration.version}-linux-${architecture}.deb`;
26-
info("Building package " + packageName);
109+
const packageExt = format === 'deb' ? 'deb' : 'rpm';
110+
const arch = mapArchitecture(configuration.arch, format);
111+
const packageName = `quarto-${configuration.version}-linux-${arch}.${packageExt}`;
112+
113+
info(`Building ${format.toUpperCase()} package: ${packageName}`);
27114

28115
// Prepare working directory
29116
const workingDir = join(configuration.directoryInfo.out, "working");
30117
info(`Preparing working directory ${workingDir}`);
31118
ensureDirSync(workingDir);
32119
emptyDirSync(workingDir);
33120

34-
// Copy bin into the proper path in working dir
121+
// Copy bin and share directories
35122
const workingBinPath = join(
36123
workingDir,
37124
"opt",
@@ -54,85 +141,55 @@ export async function makeInstallerDeb(
54141
overwrite: true,
55142
});
56143

57-
const val = (name: string, value: string): string => {
58-
return `${name}: ${value}\n`;
59-
};
144+
// Create copyright file for DEB packages
145+
if (format === 'deb') {
146+
info("Creating copyright file");
147+
const url = "https://github.com/quarto-dev/quarto-cli";
148+
const copyrightText = `Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
149+
Upstream-Name: Quarto
150+
Source: ${url}
60151
61-
// Calculate the install size
62-
const fileSizes = [];
63-
for await (const entry of walk(configuration.directoryInfo.pkgWorking.root)) {
64-
if (entry.isFile) {
65-
fileSizes.push((await Deno.stat(entry.path)).size);
66-
}
67-
}
68-
const size = fileSizes.reduce((accum, target) => {
69-
return accum + target;
70-
});
71-
const url = "https://github.com/quarto-dev/quarto-cli";
72-
const recommends = ["unzip"];
73-
74-
// Make the control file
75-
info("Creating control file");
76-
let control = "";
77-
control = control + val("Package", configuration.productName);
78-
if (recommends.length) {
79-
control = control + val("Recommends", recommends.join(","));
152+
Files: *
153+
Copyright: Posit, PBC.
154+
License: MIT`;
155+
156+
const copyrightDir = join(workingDir, "usr", "share", "doc", configuration.productName.toLowerCase());
157+
ensureDirSync(copyrightDir);
158+
Deno.writeTextFileSync(join(copyrightDir, "copyright"), copyrightText);
80159
}
81-
control = control + val("Version", configuration.version);
82-
control = control + val("Architecture", architecture);
83-
control = control + val("Installed-Size", `${Math.round(size / 1024)}`);
84-
control = control + val("Section", "user/text");
85-
control = control + val("Priority", "optional");
86-
control = control + val("Maintainer", "Posit, PBC <[email protected]>");
87-
control = control + val("Homepage", url);
88-
control = control +
89-
val(
90-
"Description",
91-
"Quarto is an academic, scientific, and technical publishing system built on Pandoc.",
92-
);
93-
info(control);
94160

95-
// Place
96-
const debianDir = join(workingDir, "DEBIAN");
97-
ensureDirSync(debianDir);
161+
// Create nfpm configuration
162+
const nfpmConfig = await createNfpmConfig(configuration, format, workingDir);
163+
const configPath = join(configuration.directoryInfo.out, "nfpm.yaml");
98164

99-
// Write the control file to the DEBIAN directory
100-
Deno.writeTextFileSync(join(debianDir, "control"), control);
165+
info("Creating nfpm configuration file");
166+
Deno.writeTextFileSync(configPath, yaml.dump(nfpmConfig));
101167

102-
// Generate and write a copyright file
103-
info("Creating copyright file");
104-
const copyrightLines = [];
105-
copyrightLines.push(
106-
"Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/",
107-
);
108-
copyrightLines.push("Upstream-Name: Quarto");
109-
copyrightLines.push(`Source: ${url}`);
110-
copyrightLines.push("");
111-
copyrightLines.push("Files: *");
112-
copyrightLines.push("Copyright: Posit, PBC.");
113-
copyrightLines.push("License: MIT");
114-
const copyrightText = copyrightLines.join("\n");
115-
Deno.writeTextFileSync(join(debianDir, "copyright"), copyrightText);
116-
117-
// copy the install scripts
118-
info("Copying install scripts...");
119-
copySync(
120-
join(configuration.directoryInfo.pkg, "scripts", "linux", "deb"),
121-
debianDir,
122-
{ overwrite: true },
123-
);
124-
125-
await runCmd("dpkg-deb", [
126-
"-Z",
127-
"gzip",
128-
"-z",
129-
"9",
130-
"--root-owner-group",
131-
"--build",
132-
workingDir,
133-
join(configuration.directoryInfo.out, packageName),
168+
// Build package using nfpm (assumes nfpm is installed in PATH)
169+
const outputPath = join(configuration.directoryInfo.out, packageName);
170+
await runCmd("nfpm", [
171+
"package",
172+
"--config", configPath,
173+
"--target", outputPath,
174+
"--packager", format,
134175
]);
135176

136-
// Remove the working directory
177+
info(`Package created: ${outputPath}`);
178+
179+
// Clean up
180+
Deno.removeSync(configPath);
181+
// Optionally remove working directory
137182
// Deno.removeSync(workingDir, { recursive: true });
138183
}
184+
185+
export async function makeInstallerDeb(
186+
configuration: Configuration,
187+
) {
188+
await buildPackageWithNfpm(configuration, 'deb');
189+
}
190+
191+
export async function makeInstallerRpm(
192+
configuration: Configuration,
193+
) {
194+
await buildPackageWithNfpm(configuration, 'rpm');
195+
}

0 commit comments

Comments
 (0)