utils.mts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import 'zx/globals';
  2. import { JsonMap, parse as parseToml } from '@iarna/toml';
  3. process.env.FORCE_COLOR = '3';
  4. process.env.CARGO_TERM_COLOR = 'always';
  5. export const workingDirectory = (await $`pwd`.quiet()).toString().trim();
  6. export function getAllProgramIdls(): string[] {
  7. return getAllProgramFolders().map((folder) =>
  8. path.join(workingDirectory, folder, 'idl.json')
  9. );
  10. }
  11. export function getExternalProgramOutputDir(): string {
  12. const config = getCargoMetadata()?.solana?.['external-programs-output'];
  13. return path.join(workingDirectory, config ?? 'target/deploy');
  14. }
  15. export function getExternalProgramAddresses(): string[] {
  16. const addresses = getProgramFolders().flatMap(
  17. (folder) => getCargoMetadata(folder)?.solana?.['program-dependencies'] ?? []
  18. );
  19. return Array.from(new Set(addresses));
  20. }
  21. export function getExternalAccountAddresses(): string[] {
  22. const addresses = getProgramFolders().flatMap(
  23. (folder) => getCargoMetadata(folder)?.solana?.['account-dependencies'] ?? []
  24. );
  25. return Array.from(new Set(addresses));
  26. }
  27. let didWarnAboutMissingPrograms = false;
  28. export function getProgramFolders(): string[] {
  29. let programs;
  30. if (process.env.PROGRAMS) {
  31. try {
  32. programs = JSON.parse(process.env.PROGRAMS);
  33. } catch (error) {
  34. programs = process.env.PROGRAMS.split(/\s+/);
  35. }
  36. } else {
  37. programs = getAllProgramFolders();
  38. }
  39. const filteredPrograms = programs.filter((program) =>
  40. fs.existsSync(path.join(workingDirectory, program))
  41. );
  42. if (
  43. filteredPrograms.length !== programs.length &&
  44. !didWarnAboutMissingPrograms
  45. ) {
  46. didWarnAboutMissingPrograms = true;
  47. programs
  48. .filter((program) => !filteredPrograms.includes(program))
  49. .forEach((program) => {
  50. echo(chalk.yellow(`Program not found: ${workingDirectory}/${program}`));
  51. });
  52. }
  53. return filteredPrograms;
  54. }
  55. export function getAllProgramFolders(): string[] {
  56. return getCargo().workspace['members'].filter(
  57. (member) => getCargo(member).package['metadata']?.['solana']?.['program-id']
  58. );
  59. }
  60. export function getCargo(folder?: string): JsonMap {
  61. return parseToml(
  62. fs.readFileSync(
  63. path.join(workingDirectory, folder ? folder : '.', 'Cargo.toml'),
  64. 'utf8'
  65. )
  66. );
  67. }
  68. export function getCargoMetadata(folder?: string) {
  69. const cargo = getCargo(folder);
  70. return folder ? cargo?.package?.['metadata'] : cargo?.workspace?.['metadata'];
  71. }
  72. export function getSolanaVersion(): string {
  73. return getCargoMetadata()?.cli?.solana;
  74. }
  75. export function getToolchain(operation): string {
  76. return getCargoMetadata()?.toolchains?.[operation];
  77. }
  78. export function getToolchainArgument(operation): string {
  79. const channel = getToolchain(operation);
  80. return channel ? `+${channel}` : '';
  81. }
  82. export function cliArguments(): string[] {
  83. return process.argv.slice(2);
  84. }
  85. export function popArgument(args: string[], arg: string) {
  86. const index = args.indexOf(arg);
  87. if (index >= 0) {
  88. args.splice(index, 1);
  89. }
  90. return index >= 0;
  91. }
  92. export function partitionArguments(
  93. args: string[],
  94. delimiter: string
  95. ): [string[], string[]] {
  96. const index = args.indexOf(delimiter);
  97. return index >= 0
  98. ? [args.slice(0, index), args.slice(index + 1)]
  99. : [args, []];
  100. }
  101. export function partitionArgumentsWithDefaultArgs(
  102. args: string[],
  103. delimiter: string,
  104. defaultArgs?: string[],
  105. ): [string[], string[]] {
  106. const [providedCargoArgs, providedCommandArgs] = partitionArguments(args, delimiter);
  107. if (defaultArgs) {
  108. const [defaultCargoArgs, defaultCommandArgs] = partitionArguments(defaultArgs, delimiter);
  109. return [
  110. [...defaultCargoArgs, ...providedCargoArgs],
  111. [...defaultCommandArgs, ...providedCommandArgs],
  112. ];
  113. }
  114. return [providedCargoArgs, providedCommandArgs];
  115. }
  116. export async function getInstalledSolanaVersion(): Promise<string | undefined> {
  117. try {
  118. const { stdout } = await $`solana --version`.quiet();
  119. return stdout.match(/(\d+\.\d+\.\d+)/)?.[1];
  120. } catch (error) {
  121. return '';
  122. }
  123. }
  124. export function parseCliArguments(): { command: string, libraryPath: string; args: string[] } {
  125. const command = process.argv[2];
  126. const args = process.argv.slice(3);
  127. // Extract the relative crate directory from the command-line arguments. This
  128. // is the only required argument.
  129. const relativePath = args.shift();
  130. if (!relativePath) {
  131. throw new Error('Missing relative manifest path');
  132. }
  133. return {
  134. command,
  135. libraryPath: path.join(workingDirectory, relativePath),
  136. args,
  137. };
  138. }