packages.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. var fs = require('graceful-fs');
  2. var path = require('path');
  3. var Q = require('q');
  4. var semver = require('semver');
  5. var mout = require('mout');
  6. var rimraf = require('rimraf');
  7. var mkdirp = require('mkdirp');
  8. var chalk = require('chalk');
  9. var cmd = require('../lib/util/cmd');
  10. var packages = require('./packages.json');
  11. var nopt = require('nopt');
  12. var options = nopt({
  13. 'force': Boolean
  14. }, {
  15. 'f': '--force'
  16. });
  17. var env = {
  18. 'GIT_AUTHOR_DATE': 'Sun Apr 7 22:13:13 2013 +0000',
  19. 'GIT_AUTHOR_NAME': 'André Cruz',
  20. 'GIT_AUTHOR_EMAIL': 'amdfcruz@gmail.com',
  21. 'GIT_COMMITTER_DATE': 'Sun Apr 7 22:13:13 2013 +0000',
  22. 'GIT_COMMITTER_NAME': 'André Cruz',
  23. 'GIT_COMMITTER_EMAIL': 'amdfcruz@gmail.com'
  24. };
  25. // Preserve the original environment
  26. mout.object.mixIn(env, process.env);
  27. function ensurePackage(dir) {
  28. var promise;
  29. // If force is specified, delete folder
  30. if (options.force) {
  31. promise = Q.nfcall(rimraf, dir)
  32. .then(function () {
  33. throw new Error();
  34. });
  35. // Otherwise check if .git is already created
  36. } else {
  37. promise = Q.nfcall(fs.stat, path.join(dir, '.git'));
  38. }
  39. // Only create if stat failed
  40. return promise.fail(function () {
  41. // Create dir
  42. return Q.nfcall(mkdirp, dir)
  43. // Init git repo
  44. .then(cmd.bind(null, 'git', ['init'], { cwd: dir }))
  45. // Create dummy file
  46. .then(function () {
  47. return Q.nfcall(fs.writeFile, path.join(dir, '.master'), 'based on master');
  48. })
  49. // Stage files
  50. .then(cmd.bind(null, 'git', ['add', '-A'], { cwd: dir }))
  51. // Commit
  52. // Note that we force a specific date and author so that the same
  53. // commit-sha's are always equal
  54. // These commit-sha's are used internally in tests!
  55. .then(function () {
  56. return cmd('git', ['commit', '-m"Initial commit."'], {
  57. cwd: dir,
  58. env: env
  59. });
  60. })
  61. .then(function () {
  62. return dir;
  63. });
  64. });
  65. }
  66. function checkRelease(dir, release) {
  67. if (semver.valid(release)) {
  68. return cmd('git', ['tag', '-l'], { cwd: dir })
  69. .spread(function (stdout) {
  70. return stdout.split(/\s*\r*\n\s*/).some(function (tag) {
  71. return semver.clean(tag) === release;
  72. });
  73. });
  74. }
  75. return cmd('git', ['branch', '--list'], { cwd: dir })
  76. .spread(function (stdout) {
  77. return stdout.split(/\s*\r*\n\s*/).some(function (branch) {
  78. branch = branch.trim().replace(/^\*?\s*/, '');
  79. return branch === release;
  80. });
  81. });
  82. }
  83. function createRelease(dir, release, files) {
  84. var branch = semver.valid(release) ? 'branch-' + release : release;
  85. // Checkout master
  86. return cmd('git', ['checkout', 'master', '-f'], { cwd: dir })
  87. // Attempt to delete branch, ignoring the error
  88. .then(function () {
  89. return cmd('git', ['branch', '-D', branch], { cwd: dir })
  90. .fail(function () {});
  91. })
  92. // Checkout based on master
  93. .then(cmd.bind(null, 'git', ['checkout', '-b', branch, 'master'], { cwd: dir }))
  94. // Create files
  95. .then(function () {
  96. var promise;
  97. var promises = [];
  98. mout.object.forOwn(files, function (contents, name) {
  99. name = path.join(dir, name);
  100. // Convert contents to JSON if they are not a string
  101. if (typeof contents !== 'string') {
  102. contents = JSON.stringify(contents, null, ' ');
  103. }
  104. promise = Q.nfcall(mkdirp, path.dirname(name))
  105. .then(function () {
  106. return Q.nfcall(fs.writeFile, name, contents);
  107. });
  108. promises.push(promise);
  109. });
  110. // Delete dummy .master file that is present on the master branch
  111. promise = Q.nfcall(fs.unlink, path.join(dir, '.master'));
  112. promises.push(promise);
  113. return Q.all(promises);
  114. })
  115. // Stage files
  116. .then(cmd.bind(null, 'git', ['add', '-A'], { cwd: dir }))
  117. // Commit
  118. // Note that we force a specific date and author so that the same
  119. // commit-sha's are always equal
  120. // These commit-sha's are used internally in tests!
  121. .then(function () {
  122. return cmd('git', ['commit', '-m"Commit for ' + branch + '."'], {
  123. cwd: dir,
  124. env: env
  125. });
  126. })
  127. // Tag
  128. .then(function () {
  129. if (!semver.valid(release)) {
  130. return;
  131. }
  132. return cmd('git', ['tag', '-f', release], { cwd: dir })
  133. // Delete branch (not necessary anymore)
  134. .then(cmd.bind(null, 'git', ['checkout', 'master', '-f'], { cwd: dir }))
  135. .then(cmd.bind(null, 'git', ['branch', '-D', branch], { cwd: dir }));
  136. });
  137. }
  138. var promises = [];
  139. // Process packages.json
  140. mout.object.forOwn(packages, function (pkg, name) {
  141. var promise;
  142. var dir = path.join(__dirname, 'assets', name);
  143. // Ensure package is created
  144. promise = ensurePackage(dir);
  145. promise = promise.fail(function (err) {
  146. console.log('Failed to create ' + name);
  147. console.log(err.message);
  148. });
  149. mout.object.forOwn(pkg, function (files, release) {
  150. // Check if the release already exists
  151. promise = promise.then(checkRelease.bind(null, dir, release))
  152. .then(function (exists) {
  153. // Skip it if already created
  154. if (exists) {
  155. return console.log(chalk.cyan('> ') + 'Package ' + name + '#' + release + ' already created');
  156. }
  157. // Create it based on the metadata
  158. return createRelease(dir, release, files)
  159. .then(function () {
  160. console.log(chalk.green('> ') + 'Package ' + name + '#' + release + ' successfully created');
  161. });
  162. })
  163. .fail(function (err) {
  164. console.log(chalk.red('> ') + 'Failed to create ' + name + '#' + release);
  165. console.log(err.message.trim());
  166. if (err.details) {
  167. console.log(err.details.trim());
  168. }
  169. console.log(err.stack);
  170. });
  171. });
  172. promises.push(promise);
  173. });
  174. Q.allSettled(promises, function (results) {
  175. results.forEach(function (result) {
  176. if (result.state !== 'fulfilled') {
  177. process.exit(1);
  178. }
  179. });
  180. });