├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ ├── default-branch-migration.yml │ ├── props-bot.yml │ └── test.yml ├── .gitignore ├── .jshintrc ├── .npmpackagejsonlintrc.json ├── .nvmrc ├── .prettierrc.js ├── CONTRIBUTING.md ├── LICENSE-MIT ├── README.md ├── RELEASING.md ├── lib ├── map_old_to_new_file_path.js ├── patch.js ├── regex.js └── trac.js ├── package-lock.json ├── package.json ├── tasks └── patch_wordpress.js └── test ├── .eslintrc.js ├── expected ├── custom_options ├── default_options ├── patch_wordpress_1.diff ├── patch_wordpress_2.diff ├── patch_wordpress_3.diff ├── patch_wordpress_4.diff ├── patch_wordpress_5.diff ├── patch_wordpress_6.diff └── patch_wordpress_7.diff ├── fixtures ├── 23988.html ├── 23989.html ├── 23994.html ├── 26602.2.diff ├── core.git.diff ├── core.git.index.diff ├── core.svn.diff ├── core.svn.index.diff ├── core.svn.trunk.diff ├── develop.git.diff ├── develop.git.index.diff ├── develop.git.wp-config-sample.diff ├── develop.svn.diff ├── develop.svn.index.diff ├── develop.svn.wp-config-sample.diff ├── git.diff.ab.diff ├── patch_wordpress_1.diff ├── patch_wordpress_2.diff ├── patch_wordpress_3.diff ├── patch_wordpress_4.diff ├── patch_wordpress_5.diff ├── patch_wordpress_6.diff ├── patch_wordpress_7.diff ├── tests.develop.git.diff └── tests.develop.svn.diff ├── patch_wordpress_test.js ├── patches.js └── regex.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | # WordPress Coding Standards 5 | # https://make.wordpress.org/core/handbook/coding-standards/ 6 | 7 | root = true 8 | 9 | [*] 10 | charset = utf-8 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | indent_style = tab 15 | 16 | [*.yml] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | es6: true, 5 | node: true, 6 | }, 7 | extends: 'plugin:@wordpress/eslint-plugin/recommended', 8 | rules: { 9 | 'one-var': [ 'error', 'never' ], 10 | 'prefer-arrow-callback': [ 'error' ], 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /.github/workflows/default-branch-migration.yml: -------------------------------------------------------------------------------- 1 | name: Default Branch Migration 2 | on: [push] 3 | jobs: 4 | migrate_branch: 5 | name: Migrate Branch 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Migrate 9 | uses: liyanchang/default-branch-migration@v1.0.1 10 | with: 11 | # GitHub will fill in this template with the correct token 12 | # You don't need to edit this line 13 | github_token: ${{ secrets.GITHUB_TOKEN }} 14 | # TODO: Change this to the default branch you want to migrate away from 15 | previous_default: master 16 | # TODO: Change this to the default branch you want to migrate away to 17 | new_default: trunk 18 | -------------------------------------------------------------------------------- /.github/workflows/props-bot.yml: -------------------------------------------------------------------------------- 1 | name: Props Bot 2 | 3 | on: 4 | # This event runs anytime a PR is (re)opened, updated, marked ready for review, or labeled. 5 | # GitHub does not allow filtering the `labeled` event by a specific label. 6 | # However, the logic below will short-circuit the workflow when the `props-bot` label is not the one being added. 7 | # Note: The pull_request_target event is used instead of pull_request because this workflow needs permission to comment 8 | # on the pull request. Because this event grants extra permissions to `GITHUB_TOKEN`, any code changes within the PR 9 | # should be considered untrusted. See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/. 10 | pull_request_target: 11 | types: 12 | - opened 13 | - synchronize 14 | - reopened 15 | - labeled 16 | - ready_for_review 17 | # This event runs anytime a comment is added or deleted. 18 | # You cannot filter this event for PR comments only. 19 | # However, the logic below does short-circuit the workflow for issues. 20 | issue_comment: 21 | type: 22 | - created 23 | # This event will run everytime a new PR review is initially submitted. 24 | pull_request_review: 25 | types: 26 | - submitted 27 | # This event runs anytime a PR review comment is created or deleted. 28 | pull_request_review_comment: 29 | types: 30 | - created 31 | 32 | # Cancels all previous workflow runs for pull requests that have not completed. 33 | concurrency: 34 | # The concurrency group contains the workflow name and the branch name for pull requests 35 | # or the commit hash for any other events. 36 | group: ${{ github.workflow }}-${{ contains( fromJSON( '["pull_request_target", "pull_request_review", "pull_request_review_comment"]' ), github.event_name ) && github.head_ref || github.sha }} 37 | cancel-in-progress: true 38 | 39 | # Disable permissions for all available scopes by default. 40 | # Any needed permissions should be configured at the job level. 41 | permissions: {} 42 | 43 | jobs: 44 | # Compiles a list of props for a pull request. 45 | # 46 | # Performs the following steps: 47 | # - Collects a list of contributor props and leaves a comment. 48 | # - Removes the props-bot label, if necessary. 49 | props-bot: 50 | name: Generate a list of props 51 | runs-on: ubuntu-latest 52 | permissions: 53 | # The action needs permission `write` permission for PRs in order to add a comment. 54 | pull-requests: write 55 | contents: read 56 | timeout-minutes: 20 57 | # The job will run when pull requests are open, ready for review and: 58 | # 59 | # - A comment is added to the pull request. 60 | # - A review is created or commented on. 61 | # - The pull request is opened, synchronized, marked ready for review, or reopened. 62 | # - The `props-bot` label is added to the pull request. 63 | if: | 64 | ( 65 | github.event_name == 'issue_comment' && github.event.issue.pull_request || 66 | contains( fromJSON( '["pull_request_review", "pull_request_review_comment"]' ), github.event_name ) || 67 | github.event_name == 'pull_request_target' && github.event.action != 'labeled' || 68 | 'props-bot' == github.event.label.name 69 | ) && 70 | ( ! github.event.pull_request.draft && github.event.pull_request.state == 'open' || ! github.event.issue.draft && github.event.issue.state == 'open' ) 71 | 72 | steps: 73 | - name: Gather a list of contributors 74 | uses: WordPress/props-bot-action@trunk 75 | 76 | - name: Remove the props-bot label 77 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 78 | if: ${{ github.event.action == 'labeled' && 'props-bot' == github.event.label.name }} 79 | with: 80 | retries: 2 81 | retry-exempt-status-codes: 418 82 | script: | 83 | github.rest.issues.removeLabel({ 84 | owner: context.repo.owner, 85 | repo: context.repo.repo, 86 | issue_number: '${{ github.event.number }}', 87 | name: 'props-bot' 88 | }); 89 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - '**.md' 7 | push: 8 | branches: [master] 9 | paths-ignore: 10 | - '**.md' 11 | 12 | jobs: 13 | test-js: 14 | name: JavaScript 15 | 16 | runs-on: ubuntu-latest 17 | 18 | strategy: 19 | matrix: 20 | node-version: 21 | - 20 22 | - 21 23 | 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v2 27 | 28 | - name: Use Node.js ${{ matrix.node-version }} 29 | uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 30 | with: 31 | node-version: ${{ matrix.node-version }} 32 | check-latest: true 33 | 34 | - name: Install NPM dependencies and build 35 | run: npm ci 36 | 37 | - name: Run tests 38 | run: npm run test 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | tmp 4 | yarn.lock 5 | coverage 6 | .nyc_output 7 | yarn-error.log 8 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "validthis": true, 3 | "laxcomma" : true, 4 | "laxbreak" : true, 5 | "eqeqeq" : false, 6 | "eqnull" : true, 7 | "debug" : true, 8 | "devel" : true, 9 | "curly" : true, 10 | "boss" : true, 11 | "expr" : true, 12 | "node" : true, 13 | "asi" : true, 14 | "indent" : 4 15 | } 16 | -------------------------------------------------------------------------------- /.npmpackagejsonlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@wordpress/npm-package-json-lint-config", 3 | "rules": { 4 | "valid-values-license": [ "error", [ "MIT" ] ], 5 | "description-format": [ 6 | "error", 7 | { 8 | "requireCapitalFirstLetter": true, 9 | "requireEndingPeriod": true 10 | } 11 | ], 12 | "prefer-no-devDependencies": "error", 13 | "require-publishConfig": "error", 14 | "require-repository-directory": "error", 15 | "valid-values-author": [ "error", [ "The WordPress Contributors" ] ], 16 | "valid-values-publishConfig": [ 17 | "error", 18 | [ 19 | { 20 | "access": "public" 21 | } 22 | ] 23 | ] 24 | }, 25 | "overrides": [ 26 | { 27 | "patterns": [ "./package.json" ], 28 | "rules": { 29 | "require-publishConfig": "off", 30 | "require-repository-directory": "off", 31 | "prefer-no-devDependencies": "off" 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | // Import the default config file and expose it in the project root. 2 | // Useful for editor integrations. 3 | module.exports = require('@wordpress/prettier-config'); 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Grunt Patch WordPress 2 | 3 | Welcome! Thanks for your interest in contributing to Grunt Patch WordPress. 4 | 5 | Please feel free to open a ticket for support or with a feature request. 6 | 7 | When filing a bug report, please include the output when the grunt task is run in [debug mode](http://gruntjs.com/using-the-cli#debug-d) by adding ```--debug``` to your command. Please also include the project that you are using and the command that failed. 8 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Aaron Jorbin 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grunt-patch-wordpress 2 | ![Unit Tests](https://github.com/WordPress/grunt-patch-wordpress/workflows/Unit%20Tests/badge.svg) 3 | 4 | > Patch your develop-wordpress directory like a boss (also works on other trac based projects) 5 | 6 | ## Getting Started 7 | This plugin requires Grunt `~0.4.5` 8 | 9 | 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: 10 | 11 | ```shell 12 | npm install grunt-patch-wordpress --save-dev 13 | ``` 14 | 15 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript: 16 | 17 | ```js 18 | grunt.loadNpmTasks('grunt-patch-wordpress'); 19 | ``` 20 | 21 | ## The "patch_wordpress" task 22 | ```js 23 | patch_wordpress{ 24 | tracUrl: 'core.trac.wordpress.org' 25 | } 26 | ``` 27 | 28 | ## Apply a patch from the command line 29 | 30 | 1. Have a diff or a patch file in your working Directory, then run ```grunt patch```. 31 | If multiple files are found, you'll be asked which one to apply. 32 | 33 | 2. Enter a ticket number, e.g. 34 | * `grunt patch:15705` 35 | 36 | 3. Enter a ticket url, e.g. 37 | * `grunt patch:https://core.trac.wordpress.org/ticket/15705` 38 | 39 | 4. Enter a patch url, e.g. 40 | * `grunt patch:https://core.trac.wordpress.org/attachment/ticket/11817/13711.diff` 41 | 42 | 5. Enter a github url that points to a changeset such as a Pull Request, e.g. 43 | * `grunt patch:https://github.com/aaronjorbin/develop.wordpress/pull/23` 44 | 45 | ## Upload a patch from the command line 46 | 47 | After you've made changes to your local WordPress develop repository, you can upload a patch file directly to a Trac ticket. e.g. given the ticket number is 2907, 48 | 49 | ```bash 50 | grunt upload_patch:2907 51 | ``` 52 | 53 | You can also store your WordPress.org credentials in environment variables. Please exercise caution when using this option, as it may cause your credentials to be leaked! 54 | 55 | ```bash 56 | export WPORG_USERNAME=matt 57 | export WPORG_PASSWORD=MyPasswordIsVerySecure12345 58 | grunt uploadPatch:40000 59 | ``` 60 | 61 | ## Using the file_mappings option 62 | If you'd like to map old file paths in your patch to new file paths during the patching process, you can pass a file mappings object as an option. Using this option can be helpful when the file paths in the project have been changed since you've created your patch. 63 | 64 | The file mappings object should contain old file paths and the corresponding new file paths. In the Gruntfile.js of your project, this would look like this: 65 | 66 | ``` 67 | patch: { 68 | options: { 69 | file_mappings: { 70 | 'old_path1': 'new_path1', 71 | 'old_path2': 'new_path2', 72 | 'old_path3': 'new_path3', 73 | } 74 | } 75 | } 76 | ``` 77 | In this example, the patch task will look for 'old_path1', 'old_path2' and 'old_path3' in your patch and replace them during patching with 'new_path1', 'new_path2', and 'new_path3' respectively. 78 | 79 | ## Contributing 80 | 81 | Please follow the [WordPress coding standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/javascript/). 82 | 83 | * Add unit tests and documentation for any new or changed functionality. 84 | * Lint and test your code using `npm run lint` and `npm run test`. 85 | 86 | ## Release History 87 | 88 | - 0.1.0 - Initial Release 89 | - 0.1.1 - Fix bug when only one diff|patch exists in the working directory 90 | - 0.1.2 - Update wording of instructions 91 | - 0.2.0 - Add support for patches generated in more ways. Improve UX by outputing results all the time 92 | - 0.3.0 - Only keep diff when debug flag is passed. Default to selecting newest patch. Make more files patchable. Allow input during patching process incase the shell prompts the user 93 | - 0.4.0 - add upload_patch, add support for github urls 94 | - 0.4.1 - Remove Mocha as a peerdendency 95 | - 0.4.2 - set `cmd-diff` to `diff` for svn 96 | - 1.0.0 - Add filemapping option. Bump minimum node version. Change code style. Update tooling. 97 | - 2.0.0 - Bump minimum node version. Use @wordpress/scripts. Allow Credentials to be stored. Reduce Dependancy on Grunt. 98 | - 3.0.0 - Bump minimum node version to [Node 12](https://github.com/WordPress/grunt-patch-wordpress/pull/89). [Update wp-scripts](https://github.com/WordPress/grunt-patch-wordpress/pull/87) and some general formatting. Use a [custom user agent for requests](https://github.com/WordPress/grunt-patch-wordpress/pull/85). 99 | - 3.0.1 - Add support for more github URL formats. Internal: Use Github Actions instead of Travis. 100 | - 4.0.0 - Bump minimum node version and some dependencies. 101 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Releasing updates 2 | 3 | ## Manual Testing 4 | 5 | Due to the interactive nature of grunt-patch-wordpress, some manual testing is required in order to release a new version. This describes the bare minimum of testing needed to release a new version. 6 | 7 | 1) Open a ticket on WordPress Core Trac for the new version. This ticket will serve as both the test bed and for actually updating grunt-patch-wordpress. 8 | 2) Use `npm link` to test the unreleased version of grunt-patch-wordpress 9 | 3) Create a patch in WordPress to bump grunt-patch-wordpress and upload it using `npm run grunt upload_patch`. 10 | 4) Revert that file. 11 | 5) Use `npm run grunt patch` to check the file you just uploaded 12 | 13 | ## Major, Minor, or Patch 14 | 15 | Major: Something is different 16 | Minor: Something is new 17 | Patch: Something is fixed 18 | 19 | Changing the minimum node version is a major version change 20 | 21 | ## Update the Numbers/Docs 22 | 23 | 1) Bump `package.json` 24 | 2) Update Readme with new version 25 | 3) When pushing to WordPress Core, give props to everyone who contributed to grunt-patch-wordpress 26 | -------------------------------------------------------------------------------- /lib/map_old_to_new_file_path.js: -------------------------------------------------------------------------------- 1 | const grunt = require( 'grunt' ); 2 | 3 | /** 4 | * Replaces file names in the passed filePath with the file names in the fileMappings. 5 | * 6 | * @param {string} filePath The path to the file where the filenames should be replaced. 7 | * @param {Object} fileMappings The file names to replace and the file names they should be replaced with. 8 | * 9 | * @return {void} 10 | */ 11 | function mapOldToNewFilePath( filePath, fileMappings ) { 12 | const body = grunt.file.read( filePath ); 13 | let newBody; 14 | let oldPath; 15 | for ( oldPath in fileMappings ) { 16 | // Ensure only own properties are looped over. 17 | if ( ! fileMappings.hasOwnProperty( oldPath ) ) { 18 | continue; 19 | } 20 | 21 | // Regex to match the second filename of the diff header. 22 | const headerRegex = new RegExp( 23 | '((diff \\-\\-git .* )(' + oldPath + ')(\\n))', 24 | 'ig' 25 | ); 26 | 27 | // Regex to match the old and new file name of the chunks within the diff. 28 | const chunkFilenameRegex = new RegExp( 29 | '((-{3}|\\+{3})( ' + oldPath + '))', 30 | 'ig' 31 | ); 32 | 33 | if ( ! body.match( chunkFilenameRegex ) ) { 34 | continue; 35 | } 36 | 37 | const newPath = fileMappings[ oldPath ]; 38 | 39 | newBody = body.replace( chunkFilenameRegex, '$2 ' + newPath ); 40 | newBody = newBody.replace( headerRegex, '$2' + newPath + '$4' ); 41 | 42 | // Logs the mapping. 43 | if ( body !== newBody ) { 44 | grunt.log.writeln( 45 | 'Old file path ' + 46 | oldPath + 47 | ' found in patch. This path has been automatically replaced by ' + 48 | newPath + 49 | '.' 50 | ); 51 | } 52 | } 53 | 54 | // newBody only has a value when there was a match. 55 | if ( newBody ) { 56 | grunt.file.write( filePath, newBody ); 57 | } 58 | } 59 | 60 | module.exports = mapOldToNewFilePath; 61 | -------------------------------------------------------------------------------- /lib/patch.js: -------------------------------------------------------------------------------- 1 | // Ensure both arguments are strings and that str begins with starts. 2 | function startsWith( str, starts ) { 3 | return 0 === ( '' + str ).lastIndexOf( '' + starts, 0 ); 4 | } 5 | 6 | module.exports = { 7 | isAb( diff ) { 8 | let ab = false; 9 | try { 10 | diff.split( '\n' ).forEach( ( line ) => { 11 | if ( startsWith( line, 'diff --git a/' ) ) { 12 | throw true; 13 | } 14 | if ( startsWith( line, 'Index: trunk/wp-' ) ) { 15 | throw true; 16 | } 17 | } ); 18 | } catch ( e ) { 19 | ab = e; 20 | } 21 | 22 | return ab; 23 | }, 24 | 25 | /** 26 | * Check to see if we should apply the diff from the src dir 27 | * 28 | * @param {string} diff A string diff 29 | * @return {boolean} true if we should go into src to apply the diff 30 | */ 31 | moveToSrc( diff ) { 32 | let src = false; 33 | const wpDashExceptions = [ 34 | '.editorconfig', 35 | '.gitignore', 36 | '.jshintrc', 37 | '.travis.yml', 38 | 'Gruntfile.js', 39 | 'package.json', 40 | 'phpunit.xml.dist', 41 | 'wp-cli.yml', 42 | 'wp-config-sample.php', 43 | 'wp-tests-config-sample.php', 44 | ]; 45 | const noWpDashExceptions = [ 46 | 'index.php', 47 | 'license.txt', 48 | 'readme.html', 49 | 'xmlrpc.php', 50 | ]; 51 | 52 | try { 53 | diff.split( '\n' ).forEach( ( line ) => { 54 | // these are often the first line 55 | if ( 56 | startsWith( line, 'Index: src/' ) || 57 | startsWith( line, 'Index: tests/' ) || 58 | startsWith( line, 'Index: tools/' ) || 59 | startsWith( line, 'diff --git src' ) || 60 | startsWith( line, 'diff --git test' ) || 61 | startsWith( line, 'diff --git tools' ) || 62 | startsWith( line, 'diff --git a/src' ) || 63 | startsWith( line, 'diff --git a/test' ) || 64 | startsWith( line, 'diff --git a/tools' ) 65 | ) { 66 | throw false; 67 | } 68 | 69 | wpDashExceptions.forEach( ( exception ) => { 70 | if ( 71 | startsWith( line, 'Index: ' + exception ) || 72 | startsWith( line, 'diff --git ' + exception ) || 73 | startsWith( line, 'diff --git a/' + exception ) 74 | ) { 75 | throw false; 76 | } 77 | } ); 78 | 79 | noWpDashExceptions.forEach( ( exception ) => { 80 | if ( 81 | startsWith( line, 'Index: ' + exception ) || 82 | startsWith( line, 'diff --git ' + exception ) || 83 | startsWith( line, 'diff --git a/' + exception ) 84 | ) { 85 | throw true; 86 | } 87 | } ); 88 | 89 | if ( 90 | startsWith( line, 'Index: wp-' ) || 91 | startsWith( line, 'Index: trunk/wp-' ) || 92 | startsWith( line, 'diff --git wp-' ) || 93 | startsWith( line, 'diff --git a/wp-' ) 94 | ) { 95 | throw true; 96 | } 97 | } ); 98 | throw true; 99 | } catch ( l ) { 100 | src = l; 101 | } 102 | return src; 103 | }, 104 | }; 105 | -------------------------------------------------------------------------------- /lib/regex.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | patchAttachments( html ) { 3 | return html.match( 4 | /
\s*([^<]+)/g 5 | ); 6 | }, 7 | 8 | urlsFromAttachmentList( html ) { 9 | return html.match( /href="([^"]+)"/ ); 10 | }, 11 | 12 | longMatches( html ) { 13 | return html.match( //g ); 14 | }, 15 | 16 | possiblePatches( longMatches ) { 17 | return longMatches 18 | .map( ( match ) => { 19 | if ( match.match( /(patch|diff)"/ ) ) { 20 | return ( 21 | match 22 | // Remove any HTML tags. 23 | .replace( /<\/?[^>]+>/g, '' ) 24 | // Collapse consecutive whitespace characters into one space. 25 | .replace( /\s+/g, ' ' ) 26 | .trim() 27 | ); 28 | } 29 | return false; 30 | } ) 31 | .filter( Boolean ); 32 | }, 33 | 34 | localFileClean( file ) { 35 | return file.replace( '?', '' ).replace( /\s/g, '' ); 36 | }, 37 | 38 | githubConvert( url ) { 39 | const matches = url.match( 40 | /(?:github\.com|patch-diff.githubusercontent.com\/raw)\/((?:[^\/]+\/){2}pull\/[0-9]*)/ 41 | ); 42 | if ( matches ) { 43 | return `https://patch-diff.githubusercontent.com/raw/${ matches[ 1 ] }.diff`; 44 | } 45 | return false; 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /lib/trac.js: -------------------------------------------------------------------------------- 1 | const url = require( 'url' ); 2 | const grunt = require( 'grunt' ); 3 | 4 | module.exports = { 5 | convertToRaw( parsedUrl ) { 6 | grunt.log.debug( 'convertToRaw: ' + JSON.stringify( parsedUrl ) ); 7 | parsedUrl.pathname = parsedUrl.pathname.replace( 8 | /attachment/, 9 | 'raw-attachment' 10 | ); 11 | grunt.log.debug( 'converted_from_raw: ' + url.format( parsedUrl ) ); 12 | return url.format( parsedUrl ); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-patch-wordpress", 3 | "version": "4.0.0", 4 | "description": "Patch your core WordPress.", 5 | "author": "The WordPress Contributors", 6 | "license": "MIT", 7 | "keywords": [ 8 | "gruntplugin" 9 | ], 10 | "homepage": "https://github.com/wordpress/grunt-patch-wordpress", 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/wordpress/grunt-patch-wordpress.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/wordpress/grunt-patch-wordpress/issues" 17 | }, 18 | "engines": { 19 | "node": ">=20.10.0", 20 | "npm": ">=10.2.3" 21 | }, 22 | "main": "Gruntfile.js", 23 | "jest": { 24 | "testMatch": [ 25 | "**/test/**/*.js" 26 | ], 27 | "testPathIgnorePatterns": [ 28 | ".eslintrc.js" 29 | ] 30 | }, 31 | "npmPackageJsonLintConfig": { 32 | "extends": "@wordpress/npm-package-json-lint-config", 33 | "rules": { 34 | "valid-values-license": [ 35 | "error", 36 | [ 37 | "MIT" 38 | ] 39 | ] 40 | } 41 | }, 42 | "dependencies": { 43 | "grunt": "^1.0.3", 44 | "inquirer": "^5.1.0", 45 | "request": "^2.83.0", 46 | "xmlrpc": "^1.3.2" 47 | }, 48 | "devDependencies": { 49 | "@wordpress/scripts": "^30.10.0", 50 | "concurrently": "^8.2.2" 51 | }, 52 | "scripts": { 53 | "check-engines": "wp-scripts check-engines", 54 | "check-licenses": "wp-scripts check-licenses --prod", 55 | "test:coverage": "wp-scripts test-unit-js --coverage", 56 | "test:help": "wp-scripts test-unit-js --help", 57 | "test:unit": "wp-scripts test-unit-js", 58 | "test:watch": "wp-scripts test-unit-js --watch", 59 | "test": "concurrently \"npm run check-engines\" \"npm run check-licenses\" \"npm run lint\" \"npm run test:unit\"", 60 | "lint": "concurrently \"npm run lint:js\" \"npm run lint:pkg-json\"", 61 | "lint:js": "wp-scripts lint-js", 62 | "lint:fix": "wp-scripts lint-js --fix", 63 | "lint:pkg-json": "wp-scripts lint-pkg-json" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tasks/patch_wordpress.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-patch-wordpress 3 | * https://github.com/WordPress/grunt-patch-wordpress 4 | * Based on https://gist.github.com/markjaquith/4219135 5 | * 6 | * 7 | * Copyright (c) 2013 Aaron Jorbin 8 | * Licensed under the MIT license. 9 | */ 10 | 11 | const request = require( 'request' ); 12 | const exec = require( 'child_process' ).exec; 13 | const execSync = require( 'child_process' ).execSync; 14 | const spawn = require( 'child_process' ).spawn; 15 | const inquirer = require( 'inquirer' ); 16 | const url = require( 'url' ); 17 | const fs = require( 'fs' ); 18 | const trac = require( '../lib/trac.js' ); 19 | const patch = require( '../lib/patch.js' ); 20 | const regex = require( '../lib/regex.js' ); 21 | const xmlrpc = require( 'xmlrpc' ); 22 | const mapOldToNewFilePath = require( '../lib/map_old_to_new_file_path.js' ); 23 | 24 | module.exports = function ( grunt ) { 25 | let tempFile = 'wppatch.diff'; 26 | const defaults = { 27 | tracUrl: 'core.trac.wordpress.org', 28 | }; 29 | 30 | function isSvn() { 31 | return fs.existsSync( '.svn' ); 32 | } 33 | 34 | function workingDir() { 35 | return process.cwd(); 36 | } 37 | 38 | function applyPatch( patchUrl, done, options ) { 39 | grunt.verbose.write( patchUrl ); 40 | const parsedUrl = url.parse( patchUrl ); 41 | 42 | // What to do when either our patch is ready 43 | grunt.event.once( 'fileReady', ( level, moveToSrc ) => { 44 | const patchOptions = {}; 45 | const patchArgs = []; 46 | 47 | // Set patch process to use the existing I/O streams, which will output 48 | // the command's results and allow for user input on patch error 49 | patchOptions.stdio = 'inherit'; 50 | 51 | // Decide if we need to be in src 52 | if ( moveToSrc ) { 53 | patchOptions.cwd = workingDir() + '/src'; 54 | tempFile = workingDir() + '/' + tempFile; 55 | } 56 | 57 | // Set the patch command's arguments 58 | patchArgs.push( '-p' + level ); 59 | patchArgs.push( '--input=' + tempFile ); 60 | 61 | grunt.log.debug( 62 | 'patch options: ' + JSON.stringify( patchOptions ) 63 | ); 64 | grunt.log.debug( 65 | 'patch arguments: ' + JSON.stringify( patchArgs ) 66 | ); 67 | grunt.log.debug( 'patch tempFile: ' + JSON.stringify( tempFile ) ); 68 | 69 | // Maps old file paths in patches to new file paths. 70 | if ( options.file_mappings ) { 71 | mapOldToNewFilePath( tempFile, options.file_mappings ); 72 | } 73 | 74 | const patchProcess = spawn( 'patch', patchArgs, patchOptions ); 75 | 76 | patchProcess.on( 'exit', ( code, signal ) => { 77 | if ( signal ) { 78 | grunt.log.debug( 'error signal: ' + signal ); 79 | } 80 | 81 | // if debug is enabled, don't delete the file 82 | if ( grunt.option( 'debug' ) ) { 83 | grunt.log.debug( 'File Saved' ); 84 | } else { 85 | grunt.file.delete( tempFile ); 86 | } 87 | 88 | done( code ); 89 | } ); 90 | } ); 91 | 92 | // or we know we have failed 93 | grunt.event.once( 'fileFail', ( msg ) => { 94 | if ( 'string' === typeof msg ) { 95 | grunt.log.errorlns( msg ); 96 | } 97 | 98 | done( false ); 99 | } ); 100 | 101 | // if patchUrl is a github url 102 | if ( regex.githubConvert( patchUrl ) ) { 103 | const diffUrl = regex.githubConvert( patchUrl ); 104 | grunt.log.debug( 'github url detected: ' + diffUrl ); 105 | 106 | getPatch( diffUrl, options ); 107 | 108 | // if patchUrl is a full url and is a raw-attachement, just apply it 109 | } else if ( 110 | parsedUrl.hostname === options.tracUrl && 111 | parsedUrl.pathname.match( /raw-attachment/ ) 112 | ) { 113 | getPatch( patchUrl, options ); 114 | 115 | // if patchUrl is full url and is an attachment, convert it to a raw attachment 116 | } else if ( 117 | parsedUrl.hostname === options.tracUrl && 118 | parsedUrl.pathname.match( /attachment/ ) && 119 | parsedUrl.pathname.match( /(patch|diff)/ ) 120 | ) { 121 | getPatch( trac.convertToRaw( parsedUrl, options ) ); 122 | 123 | // if patchUrl is just a ticket number, get a list of patches on that ticket and allow user to choose one 124 | } else if ( 125 | parsedUrl.hostname === options.tracUrl && 126 | parsedUrl.pathname.match( /ticket/ ) 127 | ) { 128 | getPatchFromTicket( patchUrl, options ); 129 | 130 | // if we just enter a number, assume it is a ticket number 131 | } else if ( 132 | null === parsedUrl.hostname && 133 | ! parsedUrl.pathname.match( /\./ ) 134 | ) { 135 | getPatchFromTicketNumber( patchUrl, options ); 136 | 137 | // if patchUrl is a local file, just use that 138 | } else if ( null === parsedUrl.hostname ) { 139 | getLocalPatch( patchUrl, options ); 140 | 141 | // We've failed in our mission 142 | } else { 143 | grunt.event.emit( 144 | 'fileFile', 145 | 'All matching failed. Please enter a ticket url, ticket number, patch url' 146 | ); 147 | } 148 | } 149 | 150 | function getPatchFromTicketNumber( patchUrl, options ) { 151 | grunt.log.debug( 'getPatchFromTicketNumber: ' + patchUrl ); 152 | getPatchFromTicket( 153 | 'https://' + 154 | options.tracUrl + 155 | '/attachment/ticket/' + 156 | patchUrl + 157 | '/', 158 | options 159 | ); 160 | } 161 | 162 | function getPatchFromTicket( patchUrl, options ) { 163 | let matches; 164 | let longMatches; 165 | let matchUrl; 166 | let possiblePatches; 167 | 168 | grunt.log.debug( 'getPatchFromTicket: ' + patchUrl ); 169 | 170 | const requestOptions = { 171 | url: patchUrl, 172 | headers: { 173 | 'User-Agent': 174 | 'grunt-patch-wordpress; https://github.com/WordPress/grunt-patch-wordpress', 175 | }, 176 | }; 177 | request( requestOptions, ( error, response, body ) => { 178 | if ( ! error && 200 === response.statusCode ) { 179 | matches = regex.patchAttachments( body ); 180 | grunt.log.debug( 'matches: ' + JSON.stringify( matches ) ); 181 | 182 | if ( null === matches ) { 183 | grunt.event.emit( 184 | 'fileFail', 185 | patchUrl + '\ncontains no attachments' 186 | ); 187 | } else if ( 1 === matches.length ) { 188 | matchUrl = 189 | options.tracUrl + 190 | regex.urlsFromAttachmentList( matches[ 0 ] )[ 1 ]; 191 | getPatch( 192 | trac.convertToRaw( url.parse( 'https://' + matchUrl ) ), 193 | options 194 | ); 195 | } else { 196 | longMatches = regex.longMatches( body ); 197 | possiblePatches = regex.possiblePatches( longMatches ); 198 | 199 | grunt.log.debug( 200 | 'possiblePatches: ' + JSON.stringify( possiblePatches ) 201 | ); 202 | grunt.log.debug( 203 | 'longMatches: ' + JSON.stringify( longMatches ) 204 | ); 205 | inquirer 206 | .prompt( [ 207 | { 208 | type: 'list', 209 | name: 'patch_name', 210 | message: 'Please select a patch to apply', 211 | choices: possiblePatches, 212 | 213 | // preselect the most recent patch 214 | default: possiblePatches.length - 1, 215 | }, 216 | ] ) 217 | .then( ( answers ) => { 218 | grunt.log.debug( 219 | 'answers:' + JSON.stringify( answers ) 220 | ); 221 | matchUrl = 222 | options.tracUrl + 223 | regex.urlsFromAttachmentList( 224 | matches[ 225 | possiblePatches.indexOf( 226 | answers.patch_name 227 | ) 228 | ] 229 | )[ 1 ]; 230 | getPatch( 231 | trac.convertToRaw( 232 | url.parse( 'https://' + matchUrl ) 233 | ), 234 | options 235 | ); 236 | } ); 237 | } 238 | } else { 239 | // something went wrong 240 | grunt.event.emit( 241 | 'fileFail', 242 | 'getPatchFromTicket fail \n status: ' + response.statusCode 243 | ); 244 | } 245 | } ); 246 | 247 | grunt.event.emit( 'fileFile', 'method not available yet' ); 248 | } 249 | 250 | function getLocalPatch( patchUrl ) { 251 | const body = grunt.file.read( patchUrl ); 252 | const level = patch.isAb( body ) ? 1 : 0; 253 | const moveToSrc = patch.moveToSrc( body ); 254 | 255 | grunt.file.copy( patchUrl, tempFile ); 256 | grunt.event.emit( 'fileReady', level, moveToSrc ); 257 | } 258 | 259 | function getPatch( patchUrl ) { 260 | grunt.log.debug( 'getting patch: ' + patchUrl ); 261 | 262 | const requestOptions = { 263 | url: patchUrl, 264 | headers: { 265 | 'User-Agent': 266 | 'grunt-patch-wordpress; https://github.com/WordPress/grunt-patch-wordpress', 267 | }, 268 | }; 269 | request( requestOptions, ( error, response, body ) => { 270 | if ( ! error && 200 === response.statusCode ) { 271 | const level = patch.isAb( body ) ? 1 : 0; 272 | const moveToSrc = patch.moveToSrc( body ); 273 | 274 | grunt.file.write( tempFile, body ); 275 | grunt.event.emit( 'fileReady', level, moveToSrc ); 276 | } else { 277 | // something went wrong 278 | grunt.event.emit( 279 | 'fileFail', 280 | 'getPatch_fail \n status: ' + response.statusCode 281 | ); 282 | } 283 | } ); 284 | } 285 | 286 | function fileFail( done, msg ) { 287 | grunt.log.errorlns( 'Nothing to patch.' ); 288 | grunt.log.errorlns( '' ); 289 | grunt.log.errorlns( '' ); 290 | grunt.log.errorlns( 'To use this command, please:' ); 291 | grunt.log.errorlns( '' ); 292 | grunt.log.errorlns( 293 | '1) have a diff or patch in your WordPress Directory' 294 | ); 295 | grunt.log.errorlns( '' ); 296 | grunt.log.errorlns( 297 | '2) enter a ticket number, e.g. grunt patch:15705' 298 | ); 299 | grunt.log.errorlns( '' ); 300 | grunt.log.errorlns( 301 | '3) enter a ticket url, e.g. grunt patch:https://core.trac.wordpress.org/ticket/15705' 302 | ); 303 | grunt.log.errorlns( '' ); 304 | grunt.log.errorlns( 305 | '4) enter a patch url, e.g. grunt patch:https://core.trac.wordpress.org/attachment/ticket/11817/13711.diff' 306 | ); 307 | grunt.log.errorlns( '' ); 308 | 309 | if ( 'string' === typeof msg ) { 310 | grunt.verbose.errorlns( 'msg: ' + msg ); 311 | } 312 | 313 | done( false ); 314 | } 315 | 316 | function localFile( error, result, code, done, options ) { 317 | if ( ! error ) { 318 | const files = result 319 | .split( '\n' ) 320 | .filter( 321 | ( file ) => 322 | file.includes( 'patch' ) || file.includes( 'diff' ) 323 | ); 324 | grunt.log.debug( 'files: ' + JSON.stringify( files ) ); 325 | 326 | if ( 0 === files.length ) { 327 | fileFail( done ); 328 | } else if ( 1 === files.length ) { 329 | applyPatch( regex.localFileClean( files[ 0 ] ), done, options ); 330 | } else { 331 | inquirer 332 | .prompt( [ 333 | { 334 | type: 'list', 335 | name: 'file', 336 | message: 'Please select a file to apply', 337 | choices: files, 338 | }, 339 | ] ) 340 | .then( ( answers ) => { 341 | const file = regex.localFileClean( answers.file ); 342 | applyPatch( file, done, options ); 343 | } ); 344 | } 345 | } else { 346 | fileFail( done, 'local file fail' ); 347 | } 348 | } 349 | 350 | grunt.registerTask( 351 | 'patch_wordpress', 352 | 'Patch your develop-wordpress directory like a boss', 353 | function ( ticket, afterProtocal ) { 354 | const done = this.async(); 355 | const options = this.options( defaults ); 356 | 357 | // since URLs contain a : which is the seperator for grunt, we 358 | // need to reassemble the url. 359 | if ( 'undefined' !== typeof afterProtocal ) { 360 | ticket = ticket + ':' + afterProtocal; 361 | } 362 | 363 | grunt.log.debug( 'ticket: ' + ticket ); 364 | grunt.log.debug( 'options: ' + JSON.stringify( options ) ); 365 | 366 | if ( 'undefined' === typeof ticket ) { 367 | // look for diffs and patches in the root of the checkout and 368 | // prompt using inquirer to pick one 369 | 370 | const fileFinderCommand = isSvn() 371 | ? 'svn status ' 372 | : 'git ls-files --other --exclude-standard'; 373 | 374 | exec( fileFinderCommand, ( error, result, code ) => { 375 | localFile( error, result, code, done, options ); 376 | } ); 377 | } else { 378 | applyPatch( ticket, done, options ); 379 | } 380 | } 381 | ); 382 | 383 | grunt.registerTask( 384 | 'upload_patch', 385 | 'Upload the current diff of your develop-wordpress directory to Trac', 386 | function ( ticketNumber ) { 387 | const done = this.async(); 388 | const options = this.options( defaults ); 389 | 390 | grunt.log.debug( 'ticketNumber: ' + ticketNumber ); 391 | grunt.log.debug( 'options: ' + JSON.stringify( options ) ); 392 | 393 | ticketNumber = parseInt( ticketNumber, 10 ); 394 | if ( 'number' !== typeof ticketNumber ) { 395 | grunt.fail.warn( 396 | 'A ticket number is required to upload a patch.' 397 | ); 398 | } 399 | 400 | const uploadPatchWithCredentials = function ( username, password ) { 401 | const diffCommand = isSvn() 402 | ? 'svn diff --diff-cmd diff' 403 | : 'git diff HEAD'; 404 | 405 | if ( ! isSvn() ) { 406 | execSync( 'git add .' ); 407 | } 408 | 409 | exec( diffCommand, ( error, result ) => { 410 | const client = xmlrpc.createSecureClient( { 411 | hostname: options.tracUrl, 412 | port: 443, 413 | path: '/login/xmlrpc', 414 | basic_auth: { 415 | // eslint-disable-line camelcase 416 | user: username, 417 | pass: password, 418 | }, 419 | } ); 420 | client.methodCall( 421 | 'ticket.putAttachment', 422 | [ 423 | ticketNumber, 424 | ticketNumber + '.diff', 425 | '', // description. empty for now. 426 | new Buffer.from( 427 | new Buffer.from( result ).toString( 'base64' ), 428 | 'base64' 429 | ), 430 | false, // never overwrite the old file 431 | ], 432 | ( err ) => { 433 | if ( ! isSvn() ) { 434 | exec( 'git reset' ); 435 | } 436 | 437 | if ( null === err ) { 438 | grunt.log.writeln( 'Uploaded patch.' ); 439 | done(); 440 | } else { 441 | grunt.fail.warn( 442 | 'Something went wrong when attempting to upload the patch. Please confirm your credentials and the ticket number. ' + 443 | err 444 | ); 445 | } 446 | } 447 | ); 448 | } ); 449 | }; 450 | if ( process.env.WPORG_USERNAME && process.env.WPORG_PASSWORD ) { 451 | uploadPatchWithCredentials( 452 | process.env.WPORG_USERNAME, 453 | process.env.WPORG_PASSWORD 454 | ); 455 | } else { 456 | inquirer 457 | .prompt( [ 458 | { 459 | type: 'input', 460 | name: 'username', 461 | message: 'Enter your WordPress.org username', 462 | }, 463 | { 464 | type: 'password', 465 | name: 'password', 466 | message: 'Enter your WordPress.org password', 467 | }, 468 | ] ) 469 | .then( ( answers ) => { 470 | uploadPatchWithCredentials( 471 | answers.username, 472 | answers.password 473 | ); 474 | } ); 475 | } 476 | } 477 | ); 478 | }; 479 | -------------------------------------------------------------------------------- /test/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /test/expected/custom_options: -------------------------------------------------------------------------------- 1 | Testing: 1 2 3 !!! -------------------------------------------------------------------------------- /test/expected/default_options: -------------------------------------------------------------------------------- 1 | Testing, 1 2 3. -------------------------------------------------------------------------------- /test/expected/patch_wordpress_1.diff: -------------------------------------------------------------------------------- 1 | diff --git src/wp-admin/js/color-picker.js src/js/_enqueues/lib/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/js/_enqueues/lib/color-picker.js 4 | +++ src/js/_enqueues/lib/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | -------------------------------------------------------------------------------- /test/expected/patch_wordpress_2.diff: -------------------------------------------------------------------------------- 1 | diff --git src/js/_enqueues/lib/color-picker.js src/js/_enqueues/lib/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/js/_enqueues/lib/color-picker.js 4 | +++ src/js/_enqueues/lib/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | -------------------------------------------------------------------------------- /test/expected/patch_wordpress_3.diff: -------------------------------------------------------------------------------- 1 | diff --git src/wp-admin/js/nice-file.js src/wp-admin/js/nice-file.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/wp-admin/js/nice-file.js 4 | +++ src/wp-admin/js/nice-file.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | -------------------------------------------------------------------------------- /test/expected/patch_wordpress_4.diff: -------------------------------------------------------------------------------- 1 | diff --git src/wp-admin/js/color-picker.js src/js/_enqueues/lib/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/js/_enqueues/lib/color-picker.js 4 | +++ src/js/_enqueues/lib/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | diff --git src/wp-admin/js/nice-file.js src/wp-admin/js/nice-file.js 14 | index a3bca29df8..52eb36036e 100644 15 | --- src/wp-admin/js/nice-file.js 16 | +++ src/wp-admin/js/nice-file.js 17 | @@ -1,6 +1,6 @@ 18 | /* global wpColorPickerL10n */ 19 | ( function( $, undef ) { 20 | - 21 | +// Very fancy patch file. 22 | var ColorPicker, 23 | _before = '', 24 | _after = '
', 25 | -------------------------------------------------------------------------------- /test/expected/patch_wordpress_5.diff: -------------------------------------------------------------------------------- 1 | diff --git src/js/_enqueues/lib/color-picker.js src/js/_enqueues/lib/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/js/_enqueues/lib/color-picker.js 4 | +++ src/js/_enqueues/lib/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | diff --git src/wp-admin/js/nice-file.js src/wp-admin/js/nice-file.js 14 | index a3bca29df8..52eb36036e 100644 15 | --- src/wp-admin/js/nice-file.js 16 | +++ src/wp-admin/js/nice-file.js 17 | @@ -1,6 +1,6 @@ 18 | /* global wpColorPickerL10n */ 19 | ( function( $, undef ) { 20 | - 21 | +// Very fancy patch file. 22 | var ColorPicker, 23 | _before = '', 24 | _after = '
', 25 | -------------------------------------------------------------------------------- /test/expected/patch_wordpress_6.diff: -------------------------------------------------------------------------------- 1 | diff --git src/wp-admin/js/color-picker.js src/js/_enqueues/lib/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/js/_enqueues/lib/color-picker.js 4 | +++ src/js/_enqueues/lib/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | diff --git src/js/_enqueues/lib/color-picker.js src/js/_enqueues/lib/color-picker.js 14 | index a3bca29df8..52eb36036e 100644 15 | --- src/js/_enqueues/lib/color-picker.js 16 | +++ src/js/_enqueues/lib/color-picker.js 17 | @@ -1,6 +1,6 @@ 18 | /* global wpColorPickerL10n */ 19 | ( function( $, undef ) { 20 | - 21 | +// Very fancy patch file. 22 | var ColorPicker, 23 | _before = '', 24 | _after = '
', 25 | diff --git src/wp-admin/js/nice-file.js src/wp-admin/js/nice-file.js 26 | index a3bca29df8..52eb36036e 100644 27 | --- src/wp-admin/js/nice-file.js 28 | +++ src/wp-admin/js/nice-file.js 29 | @@ -1,6 +1,6 @@ 30 | /* global wpColorPickerL10n */ 31 | ( function( $, undef ) { 32 | - 33 | +// Very fancy patch file. 34 | var ColorPicker, 35 | _before = '', 36 | _after = '
', 37 | -------------------------------------------------------------------------------- /test/expected/patch_wordpress_7.diff: -------------------------------------------------------------------------------- 1 | diff --git wp-admin/js/color-picker.js js/_enqueues/lib/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- js/_enqueues/lib/color-picker.js 4 | +++ js/_enqueues/lib/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | -------------------------------------------------------------------------------- /test/fixtures/23988.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Ticket #23988 – Attachments 14 | – WordPress Trac 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 |
50 | 51 |

WordPress.org

52 | 58 |
59 | 91 |
92 |
93 |
94 |
95 |
96 |

Make WordPress Core

97 | 106 |
107 |
108 | 122 | 124 |
125 | 132 |
133 |

Ticket #23988

134 | 135 |
136 |

Attachments (4)

137 |
138 |
139 |
140 | edit-form-comment.diff 141 | (626 bytes) - 142 | added by Thaicloud 7 weeks ago. 143 |
144 |
145 | Adds "In Response To: Post Title" to comment edit page 146 |
147 |
148 | 23988-edit-comment.diff 149 | (1.1 KB) - 150 | added by seanchayes 13 days ago. 151 |
152 |
153 | 23988-edit-comment-patch.jpg 154 | (69.8 KB) - 155 | added by seanchayes 13 days ago. 156 |
157 |
158 | 23988-edit-comment-patch-02.jpg 159 | (69.3 KB) - 160 | added by seanchayes 13 days ago. 161 |
162 |
163 |

164 | Download all attachments as: .zip 165 |

166 | 167 |
168 |
169 | 170 |
171 |
172 | 206 | 218 | 219 | 220 | 221 | 222 | 223 | 227 | 232 | 238 |
239 | 269 | 270 | -------------------------------------------------------------------------------- /test/fixtures/23989.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Ticket #23989 – Attachments 14 | – WordPress Trac 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 |
50 | 51 |

WordPress.org

52 | 58 |
59 | 91 |
92 |
93 |
94 |
95 |
96 |

Make WordPress Core

97 | 106 |
107 |
108 | 122 | 124 |
125 | 132 |
133 |

Ticket #23989

134 | 135 |
136 |

Attachments (1)

137 |
138 |
139 |
140 | long-button-text.png 141 | (89.3 KB) - 142 | added by MikeHansenMe 10 months ago. 143 |
144 |
145 | long button text 146 |
147 |
148 |

149 | Download all attachments as: .zip 150 |

151 | 152 |
153 |
154 | 155 |
156 |
157 | 191 | 203 | 204 | 205 | 206 | 207 | 208 | 212 | 217 | 223 |
224 | 254 | 255 | -------------------------------------------------------------------------------- /test/fixtures/23994.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Ticket #23994 – Attachments 14 | – WordPress Trac 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 |
50 | 51 |

WordPress.org

52 | 58 |
59 | 91 |
92 |
93 |
94 |
95 |
96 |

Make WordPress Core

97 | 106 |
107 |
108 | 122 | 124 |
125 | 132 |
133 |

Ticket #23994

134 | 135 |
136 |

Attachments (1)

137 |
138 |
139 |
140 | 23994.diff 141 | (659 bytes) - 142 | added by obenland 10 months ago. 143 |
144 |
145 |

146 | Download all attachments as: .zip 147 |

148 | 149 |
150 |
151 | 152 |
153 |
154 | 188 | 200 | 201 | 202 | 203 | 204 | 205 | 209 | 214 | 220 |
221 | 251 | 252 | -------------------------------------------------------------------------------- /test/fixtures/26602.2.diff: -------------------------------------------------------------------------------- 1 | Index: src/wp-admin/themes.php 2 | =================================================================== 3 | --- src/wp-admin/themes.php (revision 26958) 4 | +++ src/wp-admin/themes.php (working copy) 5 | @@ -192,7 +192,7 @@ 6 | */ 7 | 8 | foreach ( $themes as $theme ) : ?> 9 | -
10 | +
11 | 12 |
13 | 14 | @@ -200,11 +200,11 @@ 15 | 16 |
17 | 18 | - 19 | + 20 |
21 | 22 | 23 | -

24 | +

25 | 26 |

27 | 28 | -------------------------------------------------------------------------------- /test/fixtures/core.git.diff: -------------------------------------------------------------------------------- 1 | diff --git wp-load.php wp-load.php 2 | index fb85953..18ed0f6 100644 3 | --- wp-load.php 4 | +++ wp-load.php 5 | @@ -1,4 +1,6 @@ 6 | 24 |
25 | - 26 | + 27 | <# if ( data.type === 'uploaded' ) { #> 28 | 29 | <# } else if ( data.type === 'default' ) { #> 30 | @@ -917,7 +917,7 @@ final class WP_Customize_Header_Image_Control extends WP_Customize_Image_Control 31 | 32 |
33 |
34 | - 35 | + 36 | <# if ( data.type === 'uploaded' ) { #> 37 | 38 | <# } else if ( data.type === 'default' ) { #> 39 | -------------------------------------------------------------------------------- /test/fixtures/patch_wordpress_1.diff: -------------------------------------------------------------------------------- 1 | diff --git src/wp-admin/js/color-picker.js src/wp-admin/js/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/wp-admin/js/color-picker.js 4 | +++ src/wp-admin/js/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | -------------------------------------------------------------------------------- /test/fixtures/patch_wordpress_2.diff: -------------------------------------------------------------------------------- 1 | diff --git src/js/_enqueues/lib/color-picker.js src/js/_enqueues/lib/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/js/_enqueues/lib/color-picker.js 4 | +++ src/js/_enqueues/lib/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | -------------------------------------------------------------------------------- /test/fixtures/patch_wordpress_3.diff: -------------------------------------------------------------------------------- 1 | diff --git src/wp-admin/js/nice-file.js src/wp-admin/js/nice-file.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/wp-admin/js/nice-file.js 4 | +++ src/wp-admin/js/nice-file.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | -------------------------------------------------------------------------------- /test/fixtures/patch_wordpress_4.diff: -------------------------------------------------------------------------------- 1 | diff --git src/wp-admin/js/color-picker.js src/wp-admin/js/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/wp-admin/js/color-picker.js 4 | +++ src/wp-admin/js/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | diff --git src/wp-admin/js/nice-file.js src/wp-admin/js/nice-file.js 14 | index a3bca29df8..52eb36036e 100644 15 | --- src/wp-admin/js/nice-file.js 16 | +++ src/wp-admin/js/nice-file.js 17 | @@ -1,6 +1,6 @@ 18 | /* global wpColorPickerL10n */ 19 | ( function( $, undef ) { 20 | - 21 | +// Very fancy patch file. 22 | var ColorPicker, 23 | _before = '', 24 | _after = '
', 25 | -------------------------------------------------------------------------------- /test/fixtures/patch_wordpress_5.diff: -------------------------------------------------------------------------------- 1 | diff --git src/js/_enqueues/lib/color-picker.js src/js/_enqueues/lib/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/js/_enqueues/lib/color-picker.js 4 | +++ src/js/_enqueues/lib/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | diff --git src/wp-admin/js/nice-file.js src/wp-admin/js/nice-file.js 14 | index a3bca29df8..52eb36036e 100644 15 | --- src/wp-admin/js/nice-file.js 16 | +++ src/wp-admin/js/nice-file.js 17 | @@ -1,6 +1,6 @@ 18 | /* global wpColorPickerL10n */ 19 | ( function( $, undef ) { 20 | - 21 | +// Very fancy patch file. 22 | var ColorPicker, 23 | _before = '', 24 | _after = '
', 25 | -------------------------------------------------------------------------------- /test/fixtures/patch_wordpress_6.diff: -------------------------------------------------------------------------------- 1 | diff --git src/wp-admin/js/color-picker.js src/wp-admin/js/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- src/wp-admin/js/color-picker.js 4 | +++ src/wp-admin/js/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | diff --git src/js/_enqueues/lib/color-picker.js src/js/_enqueues/lib/color-picker.js 14 | index a3bca29df8..52eb36036e 100644 15 | --- src/js/_enqueues/lib/color-picker.js 16 | +++ src/js/_enqueues/lib/color-picker.js 17 | @@ -1,6 +1,6 @@ 18 | /* global wpColorPickerL10n */ 19 | ( function( $, undef ) { 20 | - 21 | +// Very fancy patch file. 22 | var ColorPicker, 23 | _before = '', 24 | _after = '
', 25 | diff --git src/wp-admin/js/nice-file.js src/wp-admin/js/nice-file.js 26 | index a3bca29df8..52eb36036e 100644 27 | --- src/wp-admin/js/nice-file.js 28 | +++ src/wp-admin/js/nice-file.js 29 | @@ -1,6 +1,6 @@ 30 | /* global wpColorPickerL10n */ 31 | ( function( $, undef ) { 32 | - 33 | +// Very fancy patch file. 34 | var ColorPicker, 35 | _before = '', 36 | _after = '
', 37 | -------------------------------------------------------------------------------- /test/fixtures/patch_wordpress_7.diff: -------------------------------------------------------------------------------- 1 | diff --git wp-admin/js/color-picker.js wp-admin/js/color-picker.js 2 | index a3bca29df8..52eb36036e 100644 3 | --- wp-admin/js/color-picker.js 4 | +++ wp-admin/js/color-picker.js 5 | @@ -1,6 +1,6 @@ 6 | /* global wpColorPickerL10n */ 7 | ( function( $, undef ) { 8 | - 9 | +// Very fancy patch file. 10 | var ColorPicker, 11 | _before = '', 12 | _after = '
', 13 | -------------------------------------------------------------------------------- /test/fixtures/tests.develop.git.diff: -------------------------------------------------------------------------------- 1 | diff --git tests/phpunit/wp-mail-real-test.php tests/phpunit/wp-mail-real-test.php 2 | index a3b4826..de0f2e8 100644 3 | --- tests/phpunit/wp-mail-real-test.php 4 | +++ tests/phpunit/wp-mail-real-test.php 5 | @@ -1,4 +1,6 @@ 6 | "; 13 | $headers[] = "BCC: {$bcc}"; 14 | wp_mail( '', $subject, $message, $headers ); 15 | echo "Test emails sent!\n" 16 | -?> 17 | \ No newline at end of file 18 | +?> 19 | -------------------------------------------------------------------------------- /test/fixtures/tests.develop.svn.diff: -------------------------------------------------------------------------------- 1 | Index: tests/phpunit/wp-mail-real-test.php 2 | =================================================================== 3 | --- tests/phpunit/wp-mail-real-test.php (revision 27349) 4 | +++ tests/phpunit/wp-mail-real-test.php (working copy) 5 | @@ -1,4 +1,6 @@ 6 | 17 | \ No newline at end of file 18 | +?> 19 | -------------------------------------------------------------------------------- /test/patch_wordpress_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const grunt = require( 'grunt' ); 4 | const patch = require( '../lib/patch.js' ); 5 | const url = require( 'url' ); 6 | const trac = require( '../lib/trac.js' ); 7 | const mapOldToNewFilePath = require( '../lib/map_old_to_new_file_path.js' ); 8 | 9 | describe( 'grunt_patch_wordpress', () => { 10 | describe( 'initial checks', () => { 11 | it( 'a is a', () => { 12 | expect( 'a' ).toEqual( 'a' ); 13 | } ); 14 | } ); 15 | 16 | it( 'convertToRaw converts urls', () => { 17 | expect( 18 | trac.convertToRaw( 19 | url.parse( 20 | 'https://core.trac.wordpress.org/attachment/ticket/26700/26700.diff' 21 | ) 22 | ) 23 | ).toEqual( 24 | 'https://core.trac.wordpress.org/raw-attachment/ticket/26700/26700.diff' 25 | ); 26 | } ); 27 | 28 | describe( 'Level Calculator', () => { 29 | // @TODO: Find alot of patches to use here 30 | 31 | it( '26602.2.diff is 0', () => { 32 | const file = grunt.file.read( 'test/fixtures/26602.2.diff' ); 33 | expect( patch.isAb( file ) ).toBe( false ); 34 | } ); 35 | } ); 36 | 37 | describe( 'mapOldToNewFilePath', () => { 38 | const fileMappings = { 39 | 'src/wp-admin/js/color-picker.js': 40 | 'src/js/_enqueues/lib/color-picker.js', 41 | 'wp-admin/js/color-picker.js': 'js/_enqueues/lib/color-picker.js', 42 | }; 43 | 44 | describe( 'old to new', () => { 45 | beforeAll( () => { 46 | grunt.file.copy( 47 | './test/fixtures/patch_wordpress_1.diff', 48 | './test/tmp/patch_wordpress_1_copy.diff' 49 | ); 50 | mapOldToNewFilePath( 51 | './test/tmp/patch_wordpress_1_copy.diff', 52 | fileMappings 53 | ); 54 | } ); 55 | 56 | it( 'replaces old file paths with new file paths in the diff', () => { 57 | const expected = grunt.file.read( 58 | './test/expected/patch_wordpress_1.diff' 59 | ); 60 | const actual = grunt.file.read( 61 | './test/tmp/patch_wordpress_1_copy.diff' 62 | ); 63 | 64 | expect( actual ).toEqual( expected ); 65 | } ); 66 | 67 | afterAll( () => { 68 | grunt.file.delete( './test/tmp/patch_wordpress_1_copy.diff' ); 69 | } ); 70 | } ); 71 | 72 | describe( 'new stay unchanged', () => { 73 | beforeAll( () => { 74 | grunt.file.copy( 75 | './test/fixtures/patch_wordpress_2.diff', 76 | './test/tmp/patch_wordpress_2_copy.diff' 77 | ); 78 | mapOldToNewFilePath( 79 | './test/tmp/patch_wordpress_2_copy.diff', 80 | fileMappings 81 | ); 82 | } ); 83 | 84 | it( "doesn't replace new file paths", () => { 85 | const expected = grunt.file.read( 86 | './test/expected/patch_wordpress_2.diff' 87 | ); 88 | const actual = grunt.file.read( 89 | './test/tmp/patch_wordpress_2_copy.diff' 90 | ); 91 | 92 | expect( actual ).toEqual( expected ); 93 | } ); 94 | 95 | afterAll( () => { 96 | grunt.file.delete( './test/tmp/patch_wordpress_2_copy.diff' ); 97 | } ); 98 | } ); 99 | 100 | describe( 'unknown stay unchanged', () => { 101 | beforeAll( () => { 102 | grunt.file.copy( 103 | './test/fixtures/patch_wordpress_3.diff', 104 | './test/tmp/patch_wordpress_3_copy.diff' 105 | ); 106 | mapOldToNewFilePath( 107 | './test/tmp/patch_wordpress_3_copy.diff', 108 | fileMappings 109 | ); 110 | } ); 111 | 112 | it( "doesn't replace file paths that are not in the file mappings object", () => { 113 | const expected = grunt.file.read( 114 | './test/expected/patch_wordpress_3.diff' 115 | ); 116 | const actual = grunt.file.read( 117 | './test/tmp/patch_wordpress_3_copy.diff' 118 | ); 119 | 120 | expect( actual ).toEqual( expected ); 121 | } ); 122 | 123 | afterAll( () => { 124 | grunt.file.delete( './test/tmp/patch_wordpress_3_copy.diff' ); 125 | } ); 126 | } ); 127 | 128 | describe( 'new stay unchanged, old to new', () => { 129 | beforeAll( () => { 130 | grunt.file.copy( 131 | './test/fixtures/patch_wordpress_4.diff', 132 | './test/tmp/patch_wordpress_4_copy.diff' 133 | ); 134 | mapOldToNewFilePath( 135 | './test/tmp/patch_wordpress_4_copy.diff', 136 | fileMappings 137 | ); 138 | } ); 139 | 140 | it( 141 | "replaces old file paths with new file paths but doesn't replace file paths that are not " + 142 | 'in the file mappings object in a diff file with multiple diffs', 143 | () => { 144 | const expected = grunt.file.read( 145 | './test/expected/patch_wordpress_4.diff' 146 | ); 147 | const actual = grunt.file.read( 148 | './test/tmp/patch_wordpress_4_copy.diff' 149 | ); 150 | 151 | expect( actual ).toEqual( expected ); 152 | } 153 | ); 154 | 155 | afterAll( () => { 156 | grunt.file.delete( './test/tmp/patch_wordpress_4_copy.diff' ); 157 | } ); 158 | } ); 159 | 160 | describe( 'new and unknown stay unchanged', () => { 161 | beforeAll( () => { 162 | grunt.file.copy( 163 | './test/fixtures/patch_wordpress_5.diff', 164 | './test/tmp/patch_wordpress_5_copy.diff' 165 | ); 166 | mapOldToNewFilePath( 167 | './test/tmp/patch_wordpress_5_copy.diff', 168 | fileMappings 169 | ); 170 | } ); 171 | 172 | it( 173 | "doesn't replaces new file paths and file paths that are not in the file mappings object in a diff file" + 174 | ' with multiple diffs', 175 | () => { 176 | const expected = grunt.file.read( 177 | './test/expected/patch_wordpress_5.diff' 178 | ); 179 | const actual = grunt.file.read( 180 | './test/tmp/patch_wordpress_5_copy.diff' 181 | ); 182 | 183 | expect( actual ).toEqual( expected ); 184 | } 185 | ); 186 | 187 | afterAll( () => { 188 | grunt.file.delete( './test/tmp/patch_wordpress_5_copy.diff' ); 189 | } ); 190 | } ); 191 | 192 | describe( 'new and unknown stay unchanged, old to new', () => { 193 | beforeAll( () => { 194 | grunt.file.copy( 195 | './test/fixtures/patch_wordpress_6.diff', 196 | './test/tmp/patch_wordpress_6_copy.diff' 197 | ); 198 | mapOldToNewFilePath( 199 | './test/tmp/patch_wordpress_6_copy.diff', 200 | fileMappings 201 | ); 202 | } ); 203 | 204 | it( 'only replaces old file paths in a diff file with multiple diffs', () => { 205 | const expected = grunt.file.read( 206 | './test/expected/patch_wordpress_6.diff' 207 | ); 208 | const actual = grunt.file.read( 209 | './test/tmp/patch_wordpress_6_copy.diff' 210 | ); 211 | 212 | expect( actual ).toEqual( expected ); 213 | } ); 214 | 215 | afterAll( () => { 216 | grunt.file.delete( './test/tmp/patch_wordpress_6_copy.diff' ); 217 | } ); 218 | } ); 219 | 220 | // There is no src folder in core. 221 | describe( 'non-src old to new', () => { 222 | beforeAll( () => { 223 | grunt.file.copy( 224 | './test/fixtures/patch_wordpress_7.diff', 225 | './test/tmp/patch_wordpress_7_copy.diff' 226 | ); 227 | mapOldToNewFilePath( 228 | './test/tmp/patch_wordpress_7_copy.diff', 229 | fileMappings 230 | ); 231 | } ); 232 | 233 | it( 'replaces old file paths with new file paths in a diff with non-src file paths', () => { 234 | const expected = grunt.file.read( 235 | './test/expected/patch_wordpress_7.diff' 236 | ); 237 | const actual = grunt.file.read( 238 | './test/tmp/patch_wordpress_7_copy.diff' 239 | ); 240 | 241 | expect( actual ).toEqual( expected ); 242 | } ); 243 | 244 | afterAll( () => { 245 | grunt.file.delete( './test/tmp/patch_wordpress_7_copy.diff' ); 246 | } ); 247 | } ); 248 | } ); 249 | } ); 250 | -------------------------------------------------------------------------------- /test/patches.js: -------------------------------------------------------------------------------- 1 | const grunt = require( 'grunt' ); 2 | const patch = require( '../lib/patch.js' ); 3 | const coreGit = grunt.file.read( 'test/fixtures/core.git.diff' ); 4 | const coreSvn = grunt.file.read( 'test/fixtures/core.svn.diff' ); 5 | const developGit = grunt.file.read( 'test/fixtures/develop.git.diff' ); 6 | const developSvn = grunt.file.read( 'test/fixtures/develop.svn.diff' ); 7 | const coreIndexGit = grunt.file.read( 'test/fixtures/core.git.index.diff' ); 8 | const coreIndexSvn = grunt.file.read( 'test/fixtures/core.svn.index.diff' ); 9 | const developIndexGit = grunt.file.read( 10 | 'test/fixtures/develop.git.index.diff' 11 | ); 12 | const developIndexSvn = grunt.file.read( 13 | 'test/fixtures/develop.svn.index.diff' 14 | ); 15 | const developSampleGit = grunt.file.read( 16 | 'test/fixtures/develop.git.wp-config-sample.diff' 17 | ); 18 | const developSampleSvn = grunt.file.read( 19 | 'test/fixtures/develop.svn.wp-config-sample.diff' 20 | ); 21 | const testsSvn = grunt.file.read( 'test/fixtures/tests.develop.svn.diff' ); 22 | const testsGit = grunt.file.read( 'test/fixtures/tests.develop.git.diff' ); 23 | const abyes = grunt.file.read( 'test/fixtures/git.diff.ab.diff' ); 24 | const coreTrunkSvn = grunt.file.read( 'test/fixtures/core.svn.trunk.diff' ); 25 | 26 | describe( 'Patch helpers', () => { 27 | it( 'git a/b diffs should not automatticaly trigger moving to src', () => { 28 | expect( patch.moveToSrc( abyes ) ).not.toBe( true ); 29 | } ); 30 | 31 | it( 'tests diffs should always be applied in the root of the checkout', () => { 32 | expect( patch.moveToSrc( testsGit ) ).not.toBe( true ); 33 | expect( patch.moveToSrc( testsSvn ) ).not.toBe( true ); 34 | } ); 35 | 36 | it( 'dev.git diffs should always be applied in the root of the checkout', () => { 37 | expect( patch.moveToSrc( developGit ) ).not.toBe( true ); 38 | expect( patch.moveToSrc( developIndexGit ) ).not.toBe( true ); 39 | } ); 40 | 41 | it( 'dev.svn diffs should always be applied in the root of the checkout', () => { 42 | expect( patch.moveToSrc( developSvn ) ).not.toBe( true ); 43 | expect( patch.moveToSrc( developIndexSvn ) ).not.toBe( true ); 44 | } ); 45 | 46 | it( 'core.git.wordpress.org diffs should always be applied in the svn folder', () => { 47 | expect( patch.moveToSrc( coreGit ) ).toBe( true ); 48 | } ); 49 | 50 | it( 'core.svn.wordpress.org diffs should always be applied in the svn folder', () => { 51 | expect( patch.moveToSrc( coreSvn ) ).toBe( true ); 52 | } ); 53 | 54 | it( 'core.svn.wordpress.org diffs from trunk should always be applied in the src folder', () => { 55 | expect( patch.moveToSrc( coreTrunkSvn ) ).toBe( true ); 56 | expect( patch.isAb( coreTrunkSvn ) ).toBe( true ); 57 | } ); 58 | 59 | it( 'index.php should always be applied in the src folder', () => { 60 | expect( patch.moveToSrc( coreIndexSvn ) ).toBe( true ); 61 | expect( patch.moveToSrc( coreIndexGit ) ).toBe( true ); 62 | } ); 63 | 64 | it( 'wp-config-sample.php should always be applied in the root folder', () => { 65 | expect( patch.moveToSrc( developSampleSvn ) ).not.toBe( true ); 66 | expect( patch.moveToSrc( developSampleGit ) ).not.toBe( true ); 67 | } ); 68 | 69 | it( 'isAb should return true on patches with a/ b/ style', () => { 70 | expect( patch.isAb( abyes ) ).toBe( true ); 71 | } ); 72 | 73 | it( 'isAb should return false on patches without a/ b/ style', () => { 74 | expect( patch.isAb( developSampleGit ) ).not.toBe( true ); 75 | expect( patch.isAb( developSampleSvn ) ).not.toBe( true ); 76 | expect( patch.isAb( coreIndexGit ) ).not.toBe( true ); 77 | expect( patch.isAb( coreIndexSvn ) ).not.toBe( true ); 78 | expect( patch.isAb( coreGit ) ).not.toBe( true ); 79 | expect( patch.isAb( coreSvn ) ).not.toBe( true ); 80 | expect( patch.isAb( developGit ) ).not.toBe( true ); 81 | expect( patch.isAb( developSvn ) ).not.toBe( true ); 82 | expect( patch.isAb( developIndexGit ) ).not.toBe( true ); 83 | expect( patch.isAb( developIndexSvn ) ).not.toBe( true ); 84 | expect( patch.isAb( testsGit ) ).not.toBe( true ); 85 | expect( patch.isAb( testsSvn ) ).not.toBe( true ); 86 | } ); 87 | } ); 88 | -------------------------------------------------------------------------------- /test/regex.js: -------------------------------------------------------------------------------- 1 | const grunt = require( 'grunt' ); 2 | const regex = require( '../lib/regex.js' ); 3 | const html23988 = grunt.file.read( 'test/fixtures/23988.html' ); 4 | const html23989 = grunt.file.read( 'test/fixtures/23989.html' ); 5 | const html23994 = grunt.file.read( 'test/fixtures/23994.html' ); 6 | 7 | describe( 'regular expressions', () => { 8 | it( 'multiple patches on a ticket with non patches as well', () => { 9 | const matches = regex.patchAttachments( html23988 ); 10 | const longMatches = regex.longMatches( html23988 ); 11 | const possiblePatches = regex.possiblePatches( longMatches ); 12 | 13 | expect( matches.length ).toBe( 2 ); 14 | expect( longMatches.length ).toBe( 4 ); 15 | expect( possiblePatches ).toEqual( [ 16 | 'edit-form-comment.diff​ (626 bytes) - added by Thaicloud 7 weeks ago.', 17 | '23988-edit-comment.diff​ (1.1 KB) - added by seanchayes 13 days ago.', 18 | ] ); 19 | } ); 20 | 21 | it( 'one patch on a ticket', () => { 22 | const matches = regex.patchAttachments( html23994 ); 23 | 24 | expect( matches.length ).toBe( 1 ); 25 | } ); 26 | 27 | it( 'url from a list of attachments', () => { 28 | const matches = regex.patchAttachments( html23994 ); 29 | const url = 30 | 'core.trac.wordpress.org' + 31 | regex.urlsFromAttachmentList( matches[ 0 ] )[ 1 ]; 32 | 33 | expect( url ).toBe( 34 | 'core.trac.wordpress.org/attachment/ticket/23994/23994.diff' 35 | ); 36 | } ); 37 | 38 | it( 'no patches on a ticket', () => { 39 | const matches = regex.patchAttachments( html23989 ); 40 | 41 | expect( matches ).toBeNull(); 42 | } ); 43 | 44 | it( 'filenames should be cleaned', () => { 45 | const filename = '? one.diff'; 46 | 47 | expect( regex.localFileClean( filename ) ).toEqual( 'one.diff' ); 48 | } ); 49 | 50 | it.each( [ 51 | [ 'https://github.com/WordPress/wordpress-develop/pull/740/', false ], // trailing slash 52 | [ 'https://github.com/WordPress/wordpress-develop/pull/740', false ], // no trailing slash 53 | [ 54 | 'https://github.com/WordPress/wordpress-develop/pull/740/checks', 55 | false, 56 | ], // checks 57 | [ 58 | 'https://github.com/WordPress/wordpress-develop/pull/740/files', 59 | false, 60 | ], // files 61 | [ 62 | 'https://github.com/WordPress/wordpress-develop/pull/740.diff', 63 | false, 64 | ], // already diffed 65 | [ 66 | 'https://github.com/WordPress/wordpress-develop/pull/740.patch', 67 | false, 68 | ], // already patched 69 | [ 70 | 'https://patch-diff.githubusercontent.com/raw/WordPress/wordpress-develop/pull/740.diff', 71 | false, 72 | ], // already diffed and redirected 73 | [ 74 | 'https://patch-diff.githubusercontent.com/raw/WordPress/wordpress-develop/pull/740.patch', 75 | false, 76 | ], // already diffed and redirected with patch 77 | [ 'https://git.com/WordPress/wordpress-develop/pull/740/files', true ], // not github url 78 | ] )( 'github url %s should get normalized', ( url, blank ) => { 79 | const expected = 80 | 'https://patch-diff.githubusercontent.com/raw/WordPress/wordpress-develop/pull/740.diff'; 81 | 82 | if ( blank ) { 83 | expect( regex.githubConvert( url ) ).toBe( false ); 84 | } else { 85 | expect( regex.githubConvert( url ) ).toEqual( expected ); 86 | } 87 | } ); 88 | } ); 89 | --------------------------------------------------------------------------------