55*
66*/
77import { 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" ;
99import { info } from "../../../src/deno_ral/log.ts" ;
10+ import * as yaml from "../../../src/core/lib/external/js-yaml.js" ;
1011
1112import { Configuration } from "../common/config.ts" ;
1213import { 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