buildLinuxPackage function

Future<String> buildLinuxPackage({
  1. required String repoRoot,
  2. required ArtifactPayload payload,
  3. required LinuxPackageConfig config,
  4. required LinuxPackageFormat format,
  5. required String outputPath,
  6. required String nfpmToolPath,
})

Builds a deb or rpm from a prebuilt payload using a caller-supplied nfpm.

File modes and the /usr/bin link are declared in the manifest rather than created on disk, so staging behaves the same on every host.

Implementation

Future<String> buildLinuxPackage({
  required String repoRoot,
  required ArtifactPayload payload,
  required LinuxPackageConfig config,
  required LinuxPackageFormat format,
  required String outputPath,
  required String nfpmToolPath,
}) async {
  if (format == LinuxPackageFormat.rpm && config.license == null) {
    throw const ShipworldException(
      'rpm packages require a license',
      code: 'invalid_config',
    );
  }

  final workDir = p.join(
    repoRoot,
    '.shipworld',
    format.packagerName,
    config.name,
  );
  final artifactPath = p.join(repoRoot, outputPath);
  final workDirectory = Directory(workDir);
  if (workDirectory.existsSync()) {
    await workDirectory.delete(recursive: true);
  }

  final stagedRoot = p.join(workDir, 'root');
  await Directory(stagedRoot).create(recursive: true);
  await Directory(p.dirname(artifactPath)).create(recursive: true);
  await payload.stage(stagedRoot);

  final desktopPath = p.join(workDir, '${config.appId}.desktop');
  await File(desktopPath).writeAsString(_installedDesktopEntry(config));

  final launcherInstallPath = p.url.join(
    config.installDirectory,
    p.url.joinAll(p.split(payload.launcherRelativePath)),
  );
  String? wrapperPath;
  if (config.launcherStyle == LinuxLauncherStyle.wrapper) {
    wrapperPath = p.join(workDir, config.executableName);
    await File(
      wrapperPath,
    ).writeAsString(_launcherWrapper(launcherInstallPath));
  }

  final stagedIcons = <({LinuxIconAsset icon, String stagedPath})>[];
  for (final icon in config.icons) {
    final stagedPath = p.join(
      workDir,
      'icons',
      '${icon.themeDirectory}${p.extension(icon.sourcePath)}',
    );
    await Directory(p.dirname(stagedPath)).create(recursive: true);
    await File(icon.sourcePath).copy(stagedPath);
    stagedIcons.add((icon: icon, stagedPath: stagedPath));
  }

  final payloadFiles =
      Directory(stagedRoot)
          .listSync(recursive: true, followLinks: false)
          .whereType<File>()
          .map((file) => p.relative(file.path, from: stagedRoot))
          .toList()
        ..sort();

  final manifestPath = p.join(workDir, 'nfpm.json');
  await File(manifestPath).writeAsString(
    const JsonEncoder.withIndent('  ').convert(
      _nfpmManifest(
        config: config,
        format: format,
        stagedRoot: stagedRoot,
        launcherInstallPath: launcherInstallPath,
        desktopPath: desktopPath,
        icons: stagedIcons,
        payloadFiles: payloadFiles,
        launcherRelativePath: payload.launcherRelativePath,
        wrapperPath: wrapperPath,
      ),
    ),
  );

  stdout.writeln('Building ${format.packagerName} package...');
  await runChecked(nfpmToolPath, <String>[
    'package',
    '--config',
    manifestPath,
    '--packager',
    format.packagerName,
    '--target',
    artifactPath,
  ], workingDirectory: repoRoot);
  return artifactPath;
}