├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── tasks └── umbraco_package.js └── test ├── Gruntfile.js ├── expected └── package.xml ├── files ├── aa332ab8-9109-2302-d683-9f06185656f1 │ └── aa332ab8-9109-2302-d683-9f06185656f1 │ │ └── package.xml ├── beec5ff0-a9a5-04ad-19b9-5cf487f240cb │ └── beec5ff0-a9a5-04ad-19b9-5cf487f240cb │ │ └── package.xml └── f7770bbb-9a09-9b66-caf0-b0e79b9321a7 │ └── f7770bbb-9a09-9b66-caf0-b0e79b9321a7 │ └── package.xml ├── fixtures ├── custom-package-xml │ └── package.xml └── example │ ├── App_Plugins │ └── TestPackage │ │ ├── package.js │ │ └── package.manifest │ └── scripts │ └── script.js └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | test/artifacts -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'stable' 4 | - '4' 5 | - '0.10' 6 | before_install: npm install -g grunt-cli 7 | install: npm install -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 - 11/24/2015 2 | 3 | #### Features: 4 | 5 | * Optionally generate `package.xml` programatically 6 | * Support for multiple build targets in grunt 7 | * Use real file names instead of guids in archive when possible 8 | * New option `outputName` to override the generated archive file name 9 | 10 | #### Misc: 11 | * General process improvements & cleanups 12 | * Introduce tests 13 | 14 | #### :bomb: Breaking Changes: 15 | 16 | This major release included a lot of breaking changes that will require you to restructure your Grunt configuration, and potentially your `package.xml` file. Here are the areas of interest: 17 | 18 | #### readme / readmeContents options merged 19 | The `readme` option has been changed to accept a string containing your desired readme content, rather than a file path to an external file. You should enter the desired content directly into this option, or use grunt templates to load the content from an external file to use the previous behavior: 20 | 21 | ```js 22 | readme: '<%= grunt.file.read("config/readme.txt") %>' 23 | ``` 24 | 25 | #### multitask 26 | The task has been converted to a multiTask to enable configuring builds for multiple packages in the same repository. To support this, you need to wrap your existing configuration with a target name, such as: 27 | 28 | ```js 29 | umbracoPackage: { 30 | main: { 31 | // normal task configuration 32 | } 33 | } 34 | ``` 35 | #### src/dest parameters 36 | To support glob patterns, the `sourceDir` and `outputDir` properties have been removed in favor of `src` and `dest`, which live at the root level of the task. You should move and rename these options per the example in the README. 37 | 38 | #### generated package.xml 39 | In the new version, the `manifest` option is no longer required, and the file can be generated automatically based on your configuration. To enable this, just remove this option and delete the `package.xml` file from your repository. 40 | 41 | If you wish to continue using your own `package.xml` file, you will need to make these changes: 42 | 43 | * Change `<%= file.guid %>.<%= file.ext %>` to `<%= file.guid %>` 44 | * Change `readmeContents` to `readme` 45 | 46 | ## 0.0.6 - 4/2/2015 47 | 48 | Maintenance 49 | * Replace `adm-zip` with `archiver` for better reliability 50 | 51 | ## 0.0.5 - 3/22/2014 52 | 53 | Fixes 54 | * Use Mac-friendly paths 55 | 56 | ## 0.0.4 - 2/28/2014 57 | 58 | Features 59 | * Make `readme` parameter optional to match Umbraco backoffice behavior 60 | 61 | ## 0.0.3 - 2/28/2014 62 | 63 | Fixes 64 | * Fix issue with build 65 | 66 | ## 0.0.2 - 2/17/2014 67 | 68 | Maintenance 69 | * Add license 70 | 71 | ## 0.0.1 - 2/17/2014 72 | 73 | * Initial commit 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Tom Fulton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | grunt-umbraco-package [![Build Status](https://travis-ci.org/tomfulton/grunt-umbraco-package.svg?branch=master)](https://travis-ci.org/tomfulton/grunt-umbraco-package) 2 | ===================== 3 | 4 | > A grunt task to automate the creation of installable Umbraco packages from your files. 5 | 6 | ## Getting Started 7 | If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command: 8 | 9 | ```shell 10 | npm install grunt-umbraco-package --save-dev 11 | ``` 12 | 13 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript: 14 | 15 | ```js 16 | grunt.loadNpmTasks('grunt-umbraco-package'); 17 | ``` 18 | 19 | 20 | ## Usage Example 21 | 22 | ```js 23 | grunt.initConfig({ 24 | pkg: grunt.file.readJSON('package.json'), 25 | umbracoPackage: { 26 | dist: { 27 | src: 'dist', // Path to a folder containing the files to be packaged 28 | dest: 'pkg', // Path to a folder to create the package file 29 | options: { // Options for the package.xml manifest 30 | name: 'My Awesome Package', 31 | version: '0.0.1', 32 | url: 'http://our.umbraco.org/projects/developer-tools/my-awesome-package', 33 | license: 'MIT', 34 | licenseUrl: 'https://opensource.org/licenses/MIT', 35 | author: 'Benetton Concubine', 36 | authorUrl: 'http://our.umbraco.org/member/1234', 37 | readme: 'Please read this!' 38 | } 39 | } 40 | } 41 | }); 42 | ``` 43 | 44 | ## Options 45 | ### Files 46 | #### src 47 | **type:** string | **required** 48 | The path to the folder containing the files to be packaged. Files should be in the desired structure to install on the target Umbraco site. 49 | 50 | #### dest 51 | **type:** string | **required** 52 | The directory path to create the generated package file. 53 | 54 | ### Manifest Options 55 | #### name 56 | **type:** string | **required** 57 | The name of your package. Used in the package manifest and to generate the package file name. 58 | 59 | #### version 60 | **type:** string | **required** 61 | The version number of your package. Used in the package manifest and to generate the package file name. 62 | 63 | #### url 64 | **type:** string | **required** 65 | The URL of your package, for the package manifest. 66 | 67 | #### license 68 | **type:** string | **required** 69 | The name of your license (ex: "MIT"), for the package manifest 70 | 71 | #### licenseUrl 72 | **type:** string | **required** 73 | The URL to your license details, for the package manifest. 74 | 75 | #### author 76 | **type:** string | **required** 77 | The name of the package author, for the package manifest. 78 | 79 | #### authorUrl 80 | **type:** string | **required** 81 | The URL of the package author, for the package manifest 82 | 83 | #### manifest 84 | **type:** string | **optional** 85 | Leave empty to generate a `package.xml` manifest for your package automatically, based on the configuration above. Optionally specify a path to an existing `package.xml` to use instead. 86 | 87 | #### readme 88 | **type:** string | **optional** 89 | Contents to use for the `readme` field of the `package.xml` manifest. 90 | 91 | #### outputName 92 | **type:** string | **optional** 93 | Leave empty to have the package file name generated as `{name}_{version}.zip`. Optionally specify a custom name to use here. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-umbraco-package", 3 | "version": "1.0.0", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/tomfulton/grunt-umbraco-package.git" 7 | }, 8 | "description": "A Grunt task to assist with creating Umbraco packages that can be installed through the Backoffice", 9 | "author": "Tom Fulton", 10 | "license": "MIT", 11 | "dependencies": { 12 | "archiver": "^0.14.3", 13 | "fs-extra": "~0.8.1", 14 | "grunt": "~0.4.2", 15 | "guid": "0.0.12", 16 | "path": "~0.4.9", 17 | "rimraf": "~2.2.6", 18 | "chalk": "^1.0.0", 19 | "js2xmlparser": "https://github.com/tomfulton/node-js2xmlparser/tarball/85ad382d102a9c21c75a39e20017dd5d8e44ace2" 20 | }, 21 | "devDependencies": { 22 | "diff": "^2.2.1", 23 | "fs": "0.0.2", 24 | "grunt": "^0.4.5", 25 | "grunt-contrib-clean": "^0.7.0", 26 | "grunt-contrib-nodeunit": "^0.4.1", 27 | "nodeunit": "^0.9.1", 28 | "path": "^0.4.10", 29 | "unzip": "^0.1.11" 30 | }, 31 | "scripts": { 32 | "test": "grunt --gruntfile test/Gruntfile.js" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tasks/umbraco_package.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | grunt.registerMultiTask('umbracoPackage', 'Create Umbraco Package', function () { 3 | var done = this.async(); 4 | var Guid = require('guid'); 5 | var path = require('path'); 6 | var rimraf = require('rimraf'); 7 | var archiver = require('archiver'); 8 | var fs = require('fs'); 9 | var fse = require('fs-extra'); 10 | var chalk = require('chalk'); 11 | var xml = require('js2xmlparser'); 12 | 13 | var options = this.options({ 14 | minimumUmbracoVersion: '', 15 | files: [], 16 | cwd: '/', 17 | readme: '' 18 | }); 19 | 20 | requireOptions(['name', 'version', 'license', 'licenseUrl', 'url', 'author', 'authorUrl'], options); 21 | validateDirectories(this.files, options); 22 | 23 | // Declare the name of the generated ZIP file 24 | var packageFileName = options.outputName ? options.outputName : options.name + '_' + options.version + '.zip'; 25 | 26 | // Ensure output directory exists 27 | if (!fs.existsSync(options.outputDir)) { 28 | fs.mkdirSync(options.outputDir); 29 | } 30 | 31 | // Declare the path of the generated ZIP file 32 | var tmpOutput = path.join(options.outputDir, packageFileName); 33 | 34 | // Delete the ZIP file if it already exists (eg. already been generated for same version) 35 | if (fs.existsSync(tmpOutput)) fs.unlinkSync(tmpOutput); 36 | 37 | // Gather files 38 | var filesToPackage = []; 39 | this.files.forEach(function (file) { 40 | file.src.forEach(function (sourceDir) { 41 | var sourceDirFiles = getFilesRecursive(sourceDir); 42 | filesToPackage.push.apply(filesToPackage, sourceDirFiles.map(function (f) { 43 | return { 44 | guid: f.name, 45 | dir: f.dir.replace(sourceDir, ''), 46 | sourceDir: sourceDir, 47 | name: f.name, 48 | ext: f.name.split('.').pop() 49 | }; 50 | })); 51 | }); 52 | }); 53 | 54 | // Avoid duplicate GUIDs 55 | var guids = {}; 56 | filesToPackage.forEach(function(file) { 57 | if (guids[file.guid]) { 58 | file.guid = Guid.raw() + '_' + file.guid; 59 | } 60 | guids[file.guid] = true; 61 | }); 62 | 63 | // Initialize the archive and it's output stream 64 | var output = fs.createWriteStream(tmpOutput); 65 | var archive = archiver('zip'); 66 | 67 | // Listen for when the ZIP is closed 68 | output.on('close', function () { 69 | console.log('Package created at ' + chalk.cyan(tmpOutput) + " (" + chalk.cyan(archive.pointer()).toString() + " bytes)"); 70 | done(true); 71 | }); 72 | 73 | // Listen for ZIP errors 74 | archive.on('error', function (err) { 75 | throw err; 76 | }); 77 | 78 | // Set the output stream of the ZIP 79 | archive.pipe(output); 80 | 81 | if (options.manifest) { 82 | 83 | // Load / transform XML Manifest 84 | options.files = filesToPackage; 85 | var manifest = grunt.file.read(options.manifest); 86 | manifest = grunt.template.process(manifest, { data: options }); 87 | 88 | // Append the manifest to the ZIP file 89 | archive.append(manifest, { name:'package.xml' }); 90 | 91 | // Append files from the source directory 92 | filesToPackage.forEach(function(file) { 93 | 94 | // Get the original path of the file 95 | var src = path.join(file.sourceDir, file.dir, file.name); 96 | 97 | // Append the file to the ZIP 98 | archive.append(fs.createReadStream(src), { name: file.guid }); 99 | 100 | }); 101 | 102 | } else { 103 | 104 | // Build the data for the package manifest 105 | var data = { 106 | info: { 107 | package: { 108 | name: options.name, 109 | version: options.version, 110 | license: { 111 | '@': { 112 | url: '' 113 | }, 114 | '#': options.license 115 | }, 116 | url: options.url, 117 | requirements: { 118 | major: 0, 119 | minor: 0, 120 | patch: 0 121 | } 122 | }, 123 | author: { 124 | name: options.author 125 | }, 126 | readme: options.readme 127 | }, 128 | DocumentTypes: {}, 129 | Templates: {}, 130 | Stylesheets: {}, 131 | Macros: {}, 132 | DictionaryItems: {}, 133 | Languages: {}, 134 | DataTypes: {}, 135 | control: {}, 136 | Actions: {}, 137 | files: {}, 138 | }; 139 | 140 | // Set optional URLs in the manifest 141 | if (options.licenseUrl) data.info.package.license['@'].url = options.licenseUrl; 142 | if (options.authorUrl) data.info.author['website'] = options.authorUrl; 143 | 144 | // Add files to the ZIP and manifest 145 | if (filesToPackage.length > 0) { 146 | data.files = []; 147 | 148 | filesToPackage.forEach(function(file) { 149 | 150 | // Get the original path of the file 151 | var src = path.join(file.sourceDir, file.dir, file.name); 152 | 153 | // Append the file to the ZIP 154 | archive.append(fs.createReadStream(src), { name: file.guid }); 155 | 156 | // Append the file to the manifest 157 | data.files.push({ 158 | guid: file.guid, 159 | orgPath: file.dir, 160 | orgName: file.name 161 | }); 162 | 163 | }); 164 | 165 | } 166 | 167 | // Append the manifest to the ZIP file 168 | archive.append(xml('umbPackage', data, { 169 | arrayMap: { 170 | files: 'file' 171 | }, 172 | prettyPrinting: { 173 | enabled: true, 174 | returnString: "\r\n" 175 | } 176 | }), { name:'package.xml' }); 177 | 178 | } 179 | 180 | archive.finalize(); 181 | 182 | function getFilesRecursive(dir) { 183 | var ret = []; 184 | var files = fs.readdirSync(dir); 185 | for (var i in files) { 186 | if (!files.hasOwnProperty(i)) continue; 187 | 188 | var name = dir + '/' + files[i]; 189 | if (fs.statSync(name).isDirectory()) { 190 | ret.push.apply(ret, getFilesRecursive(name)); 191 | } else { 192 | ret.push({ dir: dir, name: files[i] }); 193 | } 194 | } 195 | return ret; 196 | } 197 | 198 | function requireOptions (opts, options) { 199 | opts.forEach(function (opt) { 200 | if (!options.hasOwnProperty(opt) || options[opt] == null || options[opt].toString().length == 0) { 201 | grunt.fail.warn('Error creating Umbraco Package - required property missing: ' + opt); 202 | return; 203 | } 204 | }) 205 | }; 206 | 207 | function validateDirectories (files, options) { 208 | if (files.length < 1) { 209 | grunt.fail.warn('Error creating Umbraco Package - no source specified'); 210 | return; 211 | } 212 | var src = files[0].src[0]; 213 | var dest = files[0].dest; 214 | if (src == null || src.length == 0) { 215 | grunt.fail.warn('Error creating Umbraco Package - no source specified'); 216 | return; 217 | } 218 | if (dest == null || dest.length == 0) { 219 | grunt.fail.warn('Error creating Umbraco Package - no source specified'); 220 | return; 221 | } 222 | options.sourceDir = src; 223 | options.outputDir = dest; 224 | } 225 | 226 | }); 227 | }; -------------------------------------------------------------------------------- /test/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | grunt.initConfig({ 4 | umbracoPackage: { 5 | example1: { 6 | src: 'fixtures/example', 7 | dest: 'artifacts', 8 | options: { 9 | name: 'TestPackage', 10 | version: '0.0.1', 11 | url: 'http://www.google.com', 12 | license: 'MIT', 13 | licenseUrl: 'http://#', 14 | author: 'John', 15 | authorUrl: 'http://john.com', 16 | readme: 'Hello' 17 | } 18 | }, 19 | example2: { 20 | src: 'fixtures/example', 21 | dest: 'artifacts', 22 | options: { 23 | name: 'TestPackage2', 24 | version: '0.0.2', 25 | url: 'http://www.google.com', 26 | license: 'MIT', 27 | licenseUrl: 'http://#', 28 | author: 'John', 29 | authorUrl: 'http://john.com', 30 | manifest: 'fixtures/custom-package-xml/package.xml' 31 | } 32 | } 33 | }, 34 | nodeunit: { 35 | all: ['test.js'], 36 | options: { 37 | reporter: 'grunt', 38 | reporterOptions: { 39 | output: 'outputdir' 40 | } 41 | } 42 | }, 43 | clean: { 44 | test: ['artifacts/*.*'] 45 | } 46 | }); 47 | 48 | grunt.loadTasks('../tasks'); 49 | grunt.loadTasks('../node_modules/grunt-contrib-nodeunit/tasks'); 50 | grunt.loadTasks('../node_modules/grunt-contrib-clean/tasks'); 51 | 52 | grunt.registerTask('setup', ['umbracoPackage']); 53 | grunt.registerTask('teardown', ['clean']); 54 | 55 | grunt.registerTask('default', ['setup', 'nodeunit', 'teardown']); 56 | } -------------------------------------------------------------------------------- /test/expected/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TestPackage 6 | 0.0.1 7 | MIT 8 | http://www.google.com 9 | 10 | 0 11 | 0 12 | 0 13 | 14 | 15 | 16 | John 17 | http://john.com 18 | 19 | Hello 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | package.js 33 | /App_Plugins/TestPackage 34 | package.js 35 | 36 | 37 | package.manifest 38 | /App_Plugins/TestPackage 39 | package.manifest 40 | 41 | 42 | script.js 43 | /scripts 44 | script.js 45 | 46 | 47 | -------------------------------------------------------------------------------- /test/files/aa332ab8-9109-2302-d683-9f06185656f1/aa332ab8-9109-2302-d683-9f06185656f1/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Archetype 6 | 1.0.0 7 | MIT 8 | https://github.com/imulus/Archetype 9 | 10 | 0 11 | 0 12 | 0 13 | 14 | 15 | 16 | Imulus 17 | http://imulus.com/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/files/beec5ff0-a9a5-04ad-19b9-5cf487f240cb/beec5ff0-a9a5-04ad-19b9-5cf487f240cb/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Archetype 6 | 1.0.0 7 | MIT 8 | https://github.com/imulus/Archetype 9 | 10 | 0 11 | 0 12 | 0 13 | 14 | 15 | 16 | Imulus 17 | http://imulus.com/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/files/f7770bbb-9a09-9b66-caf0-b0e79b9321a7/f7770bbb-9a09-9b66-caf0-b0e79b9321a7/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Archetype 6 | 1.0.0 7 | MIT 8 | https://github.com/imulus/Archetype 9 | 10 | 0 11 | 0 12 | 0 13 | 14 | 15 | 16 | Imulus 17 | http://imulus.com/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/fixtures/custom-package-xml/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= name %> 6 | <%= version %> 7 | <%= license %> 8 | <%= url %> 9 | 10 | 0 11 | 0 12 | 0 13 | 14 | 15 | 16 | <%= author %> 17 | <%= authorUrl %> 18 | 19 | ]]> 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | <% files.forEach(function(file) { %> 32 | 33 | <%= file.guid %> 34 | <%= file.dir %> 35 | <%= file.name %> 36 | 37 | <% }); %> 38 | 39 | -------------------------------------------------------------------------------- /test/fixtures/example/App_Plugins/TestPackage/package.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomfulton/grunt-umbraco-package/d1dc9823042d63f35eb40869b4a45d7942cdebd1/test/fixtures/example/App_Plugins/TestPackage/package.js -------------------------------------------------------------------------------- /test/fixtures/example/App_Plugins/TestPackage/package.manifest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomfulton/grunt-umbraco-package/d1dc9823042d63f35eb40869b4a45d7942cdebd1/test/fixtures/example/App_Plugins/TestPackage/package.manifest -------------------------------------------------------------------------------- /test/fixtures/example/scripts/script.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomfulton/grunt-umbraco-package/d1dc9823042d63f35eb40869b4a45d7942cdebd1/test/fixtures/example/scripts/script.js -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var JsDiff = require('diff'); 3 | var unzip = require('unzip'); 4 | var path = require('path'); 5 | 6 | module.exports = { 7 | packageCreatedWithCorrectName: function (test) { 8 | try { 9 | var file1 = fs.lstatSync(path.join('artifacts', 'TestPackage_0.0.1.zip')); 10 | var file2 = fs.lstatSync(path.join('artifacts', 'TestPackage2_0.0.2.zip')); 11 | 12 | test.ok(file1.isFile()); 13 | test.ok(file2.isFile()); 14 | } catch (e) { 15 | test.ok(false); 16 | } 17 | test.done(); 18 | }, 19 | packageContainsCorrectFiles: function (test) { 20 | var actual = []; 21 | var expected = ['package.manifest', 'package.js', 'script.js', 'package.xml']; 22 | 23 | var parse = unzip.Parse(); 24 | fs.createReadStream(path.join('artifacts', 'TestPackage_0.0.1.zip')).pipe(parse); 25 | parse.on('entry', function(entry) { 26 | actual.push(entry.path); 27 | }); 28 | parse.on('close', function() { 29 | actual.sort(); 30 | expected.sort(); 31 | test.deepEqual(actual, expected, 'package zip file should unzip and contain all of the expected files'); 32 | test.done(); 33 | }); 34 | }, 35 | packageXmlManifestGenerated: function (test) { 36 | var parse = unzip.Parse(); 37 | fs.createReadStream(path.join('artifacts', 'TestPackage_0.0.1.zip')).pipe(parse); 38 | parse.on('entry', function(entry) { 39 | if (entry.path == 'package.xml') { 40 | entry 41 | .pipe(fs.createWriteStream(path.join('artifacts', 'extracted-package-xml.xml'))) 42 | .on('close', function() { 43 | var file1Contents = fs.readFileSync(path.join('expected', 'package.xml')).toString(); 44 | var file2Contents = fs.readFileSync(path.join('artifacts', 'extracted-package-xml.xml')).toString(); 45 | var diff = JsDiff.diffLines(file1Contents, file2Contents, { ignoreWhitespace: true, newlineIsToken: true }); 46 | 47 | var anyChanges = false; 48 | diff.forEach(function(part) { 49 | if (part.added || part.removed) { 50 | anyChanges = true; 51 | } 52 | }); 53 | 54 | test.ok(!anyChanges); 55 | test.done(); 56 | }); 57 | } 58 | }); 59 | } 60 | }; --------------------------------------------------------------------------------