NsisVersions.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import { basename, dirname, relative } from 'path';
  2. import { createHash } from 'crypto';
  3. import { exists, readJsonAsync, writeJsonAsync, createReadStream } from 'fs-extra-promise';
  4. import * as semver from 'semver';
  5. interface IInstaller {
  6. arch: string;
  7. path: string;
  8. hash: string;
  9. created: number;
  10. }
  11. interface IUpdater {
  12. arch: string;
  13. fromVersion: string;
  14. path: string;
  15. hash: string;
  16. created: number;
  17. }
  18. interface IVersion {
  19. version: string;
  20. changelog: string;
  21. source: string;
  22. installers: IInstaller[];
  23. updaters: IUpdater[];
  24. }
  25. interface IData {
  26. latest: string;
  27. versions: IVersion[];
  28. }
  29. export class NsisVersions {
  30. protected outputDir: string;
  31. protected data: IData;
  32. constructor(protected path: string) {
  33. this.outputDir = dirname(path);
  34. }
  35. public async addVersion(version: string, changelog: string, source: string) {
  36. const data = await this.getData();
  37. if(!data.versions.find(item => item.version == version)) {
  38. data.versions.push({
  39. version,
  40. changelog,
  41. source: basename(source),
  42. installers: [],
  43. updaters: [],
  44. });
  45. }
  46. this.updateLatestVersion();
  47. }
  48. public async getVersions(): Promise<string[]> {
  49. const data = await this.getData();
  50. return data.versions.map(item => item.version);
  51. }
  52. public async getVersion(version: string): Promise<IVersion> {
  53. const data = await this.getData();
  54. const item = data.versions.find(item => item.version == version);
  55. if(!item) {
  56. throw new Error('ERROR_VERSION_NOT_FOUND');
  57. }
  58. return item;
  59. }
  60. public async addInstaller(version: string, arch: string, path: string) {
  61. const data = await this.getData();
  62. const versionItem: IVersion = data.versions.find(item => item.version == version);
  63. if(!versionItem) {
  64. throw new Error('ERROR_VERSION_NOT_FOUND');
  65. }
  66. if(!versionItem.installers.find(item => item.arch == arch)) {
  67. versionItem.installers.push({
  68. arch,
  69. path: relative(this.outputDir, path),
  70. hash: await this.hashFile('sha256', path),
  71. created: Date.now(),
  72. });
  73. }
  74. }
  75. public async addUpdater(version: string, fromVersion: string, arch: string, path: string) {
  76. const data = await this.getData();
  77. const versionItem: IVersion = data.versions.find(item => item.version == version);
  78. if(!versionItem) {
  79. throw new Error('ERROR_VERSION_NOT_FOUND');
  80. }
  81. if(!versionItem.updaters.find(item => item.fromVersion == fromVersion && item.arch == arch)) {
  82. versionItem.updaters.push({
  83. fromVersion,
  84. arch,
  85. path: relative(this.outputDir, path),
  86. hash: await this.hashFile('sha256', path),
  87. created: Date.now(),
  88. });
  89. }
  90. }
  91. public async save() {
  92. await writeJsonAsync(this.path, this.data);
  93. }
  94. protected async getData() {
  95. if(!this.data) {
  96. this.data = (await new Promise((resolve, reject) => exists(this.path, resolve)))
  97. ? await readJsonAsync(this.path)
  98. : {
  99. latest: undefined,
  100. versions: [],
  101. };
  102. }
  103. return this.data;
  104. }
  105. protected updateLatestVersion() {
  106. if(this.data.versions.length == 0) {
  107. return;
  108. }
  109. const versions = [ ...this.data.versions ];
  110. versions.sort((a, b) => semver.gt(a.version, b.version) ? -1 : 1);
  111. this.data.latest = versions[0].version;
  112. }
  113. protected hashFile(type: string, path: string): Promise<string> {
  114. return new Promise((resolve, reject) => {
  115. const hasher = createHash(type);
  116. hasher.on('error', reject);
  117. hasher.on('readable', () => {
  118. const data = hasher.read();
  119. if(data) {
  120. hasher.end();
  121. resolve((<any>data).toString('hex'));
  122. }
  123. });
  124. createReadStream(path).pipe(hasher);
  125. });
  126. }
  127. }