publish-to-npm.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. const temp = require('temp');
  2. const fs = require('fs');
  3. const path = require('path');
  4. const childProcess = require('child_process');
  5. const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils');
  6. const request = require('request');
  7. const semver = require('semver');
  8. const rootPackageJson = require('../../package.json');
  9. const { Octokit } = require('@octokit/rest');
  10. const octokit = new Octokit({
  11. userAgent: 'electron-npm-publisher',
  12. auth: process.env.ELECTRON_GITHUB_TOKEN
  13. });
  14. if (!process.env.ELECTRON_NPM_OTP) {
  15. console.error('Please set ELECTRON_NPM_OTP');
  16. process.exit(1);
  17. }
  18. let tempDir;
  19. temp.track(); // track and cleanup files at exit
  20. const files = [
  21. 'cli.js',
  22. 'index.js',
  23. 'install.js',
  24. 'package.json',
  25. 'README.md',
  26. 'LICENSE'
  27. ];
  28. const jsonFields = [
  29. 'name',
  30. 'version',
  31. 'repository',
  32. 'description',
  33. 'license',
  34. 'author',
  35. 'keywords'
  36. ];
  37. let npmTag = '';
  38. new Promise((resolve, reject) => {
  39. temp.mkdir('electron-npm', (err, dirPath) => {
  40. if (err) {
  41. reject(err);
  42. } else {
  43. resolve(dirPath);
  44. }
  45. });
  46. })
  47. .then((dirPath) => {
  48. tempDir = dirPath;
  49. // copy files from `/npm` to temp directory
  50. files.forEach((name) => {
  51. const noThirdSegment = name === 'README.md' || name === 'LICENSE';
  52. fs.writeFileSync(
  53. path.join(tempDir, name),
  54. fs.readFileSync(path.join(ELECTRON_DIR, noThirdSegment ? '' : 'npm', name))
  55. );
  56. });
  57. // copy from root package.json to temp/package.json
  58. const packageJson = require(path.join(tempDir, 'package.json'));
  59. jsonFields.forEach((fieldName) => {
  60. packageJson[fieldName] = rootPackageJson[fieldName];
  61. });
  62. fs.writeFileSync(
  63. path.join(tempDir, 'package.json'),
  64. JSON.stringify(packageJson, null, 2)
  65. );
  66. return octokit.repos.listReleases({
  67. owner: 'electron',
  68. repo: rootPackageJson.version.indexOf('nightly') > 0 ? 'nightlies' : 'electron'
  69. });
  70. })
  71. .then((releases) => {
  72. // download electron.d.ts from release
  73. const release = releases.data.find(
  74. (release) => release.tag_name === `v${rootPackageJson.version}`
  75. );
  76. if (!release) {
  77. throw new Error(`cannot find release with tag v${rootPackageJson.version}`);
  78. }
  79. return release;
  80. })
  81. .then((release) => {
  82. const tsdAsset = release.assets.find((asset) => asset.name === 'electron.d.ts');
  83. if (!tsdAsset) {
  84. throw new Error(`cannot find electron.d.ts from v${rootPackageJson.version} release assets`);
  85. }
  86. return new Promise((resolve, reject) => {
  87. request.get({
  88. url: tsdAsset.url,
  89. headers: {
  90. accept: 'application/octet-stream',
  91. 'user-agent': 'electron-npm-publisher'
  92. }
  93. }, (err, response, body) => {
  94. if (err || response.statusCode !== 200) {
  95. reject(err || new Error('Cannot download electron.d.ts'));
  96. } else {
  97. fs.writeFileSync(path.join(tempDir, 'electron.d.ts'), body);
  98. resolve(release);
  99. }
  100. });
  101. });
  102. })
  103. .then(async (release) => {
  104. const currentBranch = await getCurrentBranch();
  105. if (release.tag_name.indexOf('nightly') > 0) {
  106. // TODO(main-migration): Simplify once main branch is renamed.
  107. if (currentBranch === 'master' || currentBranch === 'main') {
  108. // Nightlies get published to their own module, so they should be tagged as latest
  109. npmTag = 'latest';
  110. } else {
  111. npmTag = `nightly-${currentBranch}`;
  112. }
  113. const currentJson = JSON.parse(fs.readFileSync(path.join(tempDir, 'package.json'), 'utf8'));
  114. currentJson.name = 'electron-nightly';
  115. rootPackageJson.name = 'electron-nightly';
  116. fs.writeFileSync(
  117. path.join(tempDir, 'package.json'),
  118. JSON.stringify(currentJson, null, 2)
  119. );
  120. } else {
  121. if (currentBranch === 'master' || currentBranch === 'main') {
  122. // This should never happen, main releases should be nightly releases
  123. // this is here just-in-case
  124. throw new Error('Unreachable release phase, can\'t tag a non-nightly release on the main branch');
  125. } else if (!release.prerelease) {
  126. // Tag the release with a `2-0-x` style tag
  127. npmTag = currentBranch;
  128. } else {
  129. // Tag the release with a `beta-3-0-x` style tag
  130. npmTag = `beta-${currentBranch}`;
  131. }
  132. }
  133. })
  134. .then(() => childProcess.execSync('npm pack', { cwd: tempDir }))
  135. .then(() => {
  136. // test that the package can install electron prebuilt from github release
  137. const tarballPath = path.join(tempDir, `${rootPackageJson.name}-${rootPackageJson.version}.tgz`);
  138. return new Promise((resolve, reject) => {
  139. childProcess.execSync(`npm install ${tarballPath} --force --silent`, {
  140. env: Object.assign({}, process.env, { electron_config_cache: tempDir }),
  141. cwd: tempDir
  142. });
  143. resolve(tarballPath);
  144. });
  145. })
  146. .then((tarballPath) => {
  147. const existingVersionJSON = childProcess.execSync(`npm view electron@${rootPackageJson.version} --json`).toString('utf-8');
  148. // It's possible this is a re-run and we already have published the package, if not we just publish like normal
  149. if (!existingVersionJSON) {
  150. childProcess.execSync(`npm publish ${tarballPath} --tag ${npmTag} --otp=${process.env.ELECTRON_NPM_OTP}`);
  151. }
  152. })
  153. .then(() => {
  154. const currentTags = JSON.parse(childProcess.execSync('npm show electron dist-tags --json').toString());
  155. const localVersion = rootPackageJson.version;
  156. const parsedLocalVersion = semver.parse(localVersion);
  157. if (rootPackageJson.name === 'electron') {
  158. // We should only customly add dist tags for non-nightly releases where the package name is still
  159. // "electron"
  160. if (parsedLocalVersion.prerelease.length === 0 &&
  161. semver.gt(localVersion, currentTags.latest)) {
  162. childProcess.execSync(`npm dist-tag add electron@${localVersion} latest --otp=${process.env.ELECTRON_NPM_OTP}`);
  163. }
  164. if (parsedLocalVersion.prerelease[0] === 'beta' &&
  165. semver.gt(localVersion, currentTags.beta)) {
  166. childProcess.execSync(`npm dist-tag add electron@${localVersion} beta --otp=${process.env.ELECTRON_NPM_OTP}`);
  167. }
  168. }
  169. })
  170. .catch((err) => {
  171. console.error(`Error: ${err}`);
  172. process.exit(1);
  173. });