diff --git a/.cfformat.json b/.cfformat.json index 4125a2b..443ef67 100644 --- a/.cfformat.json +++ b/.cfformat.json @@ -1,13 +1,14 @@ { "array.empty_padding": false, "array.padding": true, - "array.multiline.min_length": 40, + "array.multiline.min_length": 50, "array.multiline.element_count": 2, "array.multiline.leading_comma.padding": true, "array.multiline.leading_comma": false, "alignment.consecutive.assignments": true, "alignment.consecutive.properties": true, "alignment.consecutive.params": true, + "alignment.doc_comments" : true, "brackets.padding": true, "comment.asterisks": "align", "binary_operators.padding": true, @@ -17,22 +18,22 @@ "function_call.multiline.leading_comma.padding": true, "function_call.casing.builtin": "cfdocs", "function_call.casing.userdefined": "camel", - "function_call.multiline.element_count": 2, + "function_call.multiline.element_count": 3, "function_call.multiline.leading_comma": false, - "function_call.multiline.min_length": 40, + "function_call.multiline.min_length": 50, "function_declaration.padding": true, "function_declaration.empty_padding": false, "function_declaration.multiline.leading_comma": false, "function_declaration.multiline.leading_comma.padding": true, - "function_declaration.multiline.element_count": 2, - "function_declaration.multiline.min_length": 40, + "function_declaration.multiline.element_count": 3, + "function_declaration.multiline.min_length": 50, "function_declaration.group_to_block_spacing": "compact", "function_anonymous.empty_padding": false, "function_anonymous.group_to_block_spacing": "compact", - "function_anonymous.multiline.element_count": 2, + "function_anonymous.multiline.element_count": 3, "function_anonymous.multiline.leading_comma": false, "function_anonymous.multiline.leading_comma.padding": true, - "function_anonymous.multiline.min_length": 40, + "function_anonymous.multiline.min_length": 50, "function_anonymous.padding": true, "indent_size": 4, "keywords.block_to_keyword_spacing": "spaced", @@ -41,13 +42,13 @@ "keywords.spacing_to_block": "spaced", "keywords.spacing_to_group": true, "keywords.empty_group_spacing": false, - "max_columns": 120, + "max_columns": 115, "metadata.multiline.element_count": 3, - "metadata.multiline.min_length": 40, - "method_call.chain.multiline": 3, - "newline": "\n", + "metadata.multiline.min_length": 50, + "method_call.chain.multiline" : 3, + "newline":"\n", "property.multiline.element_count": 3, - "property.multiline.min_length": 40, + "property.multiline.min_length": 30, "parentheses.padding": true, "strings.quote": "double", "strings.attributes.quote": "double", @@ -57,6 +58,6 @@ "struct.multiline.leading_comma": false, "struct.multiline.leading_comma.padding": true, "struct.multiline.element_count": 2, - "struct.multiline.min_length": 40, + "struct.multiline.min_length": 60, "tab_indent": true -} \ No newline at end of file +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8d2322c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,229 @@ +name: mementifier CI + +# Only on Development we build snapshots +on: + push: + branches: + - development + - master + +env: + MODULE_ID: mementifier + +jobs: + ############################################# + # Tests First baby! We fail, no build :( + ############################################# + tests: + name: Tests + runs-on: ubuntu-20.04 + env: + DB_USER: root + DB_PASSWORD: root + strategy: + fail-fast: false + matrix: + cfengine: [ "lucee@5", "adobe@2016", "adobe@2018", "adobe@2021" ] + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "11" + + - name: Setup Database and Fixtures + run: | + sudo systemctl start mysql.service + # Create Database + mysql -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} -e 'CREATE DATABASE mementifier;' + + - name: Setup Environment For Testing Process + working-directory: ./test-harness + run: | + # Setup .env + touch .env + # ENV + printf "DB_HOST=localhost\n" >> .env + printf "DB_USER=${{ env.DB_USER }}\n" >> .env + printf "DB_PASSWORD=${{ env.DB_PASSWORD }}\n" >> .env + printf "DB_CLASS=com.mysql.cj.jdbc.Driver\n" >> .env + printf "DB_BUNDLEVERSION=8.0.19\n" >> .env + printf "DB_BUNDLENAME=com.mysql.cj\n" >> .env + + - name: Cache CommandBox Dependencies + uses: actions/cache@v1 + if: ${{ true }} + with: + path: ~/.CommandBox/artifacts + key: ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }}-${{ hashFiles( 'test-harness/box.json' ) }} + restore-keys: | + ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }}-${{ hashFiles( 'test-harness/box.json' ) }} + + - name: Setup CommandBox + uses: elpete/setup-commandbox@v1.0.0 + + - name: Install Test Harness Dependencies + working-directory: ./test-harness + run: | + box install + + - name: Start ${{ matrix.cfengine }} Server + working-directory: ./test-harness + run: | + box server start serverConfigFile="server-${{ matrix.cfengine }}.json" --noSaveSettings --debug + # Install Adobe 2021 cfpm modules + if [[ "${{ matrix.cfengine }}" == "adobe@2021" ]] ; then + box run-script install:2021 + fi + curl http://127.0.0.1:60299 + + - name: Run Tests + working-directory: ./test-harness + run: | + mkdir tests/results + box package set testbox.runner="http://localhost:60299/tests/runner.cfm" + box testbox run --verbose outputFile=tests/results/test-results outputFormats=json,antjunit + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v1 + if: always() + with: + files: test-harness/tests/results/**/*.xml + check_name: "${{ matrix.cfengine }} Test Results" + + - name: Upload Test Results Artifacts + if: always() + uses: actions/upload-artifact@v2 + with: + name: test-results-${{ matrix.cfengine }} + path: | + test-harness/tests/results/**/* + + - name: Slack Notification + if: failure() + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_CHANNEL: coding + SLACK_COLOR: ${{ job.status }} # or a specific color like 'green' or '#ff00ff' + SLACK_ICON_EMOJI: ":bell:" + SLACK_MESSAGE: '${{ env.MODULE_ID }} tests failed :cry:' + SLACK_TITLE: ${{ env.MODULE_ID }} Tests For ${{ matrix.cfengine }} failed + SLACK_USERNAME: CI + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + + - name: Failure Debugging Info + if: ${{ failure() }} + working-directory: ./test-harness + run: | + box server log serverConfigFile="server-${{ matrix.cfengine }}.json" + + - name: Upload Debugging Info To Artifacts + if: ${{ failure() }} + uses: actions/upload-artifact@v2 + with: + name: Failure Debugging Info - ${{ matrix.cfengine }} + path: | + test-harness/.engine/**/logs/* + test-harness/.engine/**/WEB-INF/cfusion/logs/* + + ############################################# + # Build Module + ############################################# + build: + name: Build & Publish + needs: tests + runs-on: ubuntu-20.04 + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "11" + + - name: Cache CommandBox Dependencies + uses: actions/cache@v1 + if: ${{ true }} + with: + path: ~/.CommandBox/artifacts + key: ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }}-${{ hashFiles( 'test-harness/box.json' ) }} + restore-keys: | + ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }}-${{ hashFiles( 'test-harness/box.json' ) }} + + - name: Setup CommandBox + uses: elpete/setup-commandbox@v1.0.0 + with: + forgeboxAPIKey: ${{ secrets.FORGEBOX_TOKEN }} + + - name: Setup Environment Variables For Build Process + id: current_version + run: | + echo "VERSION=`cat box.json | jq '.version' -r`" >> $GITHUB_ENV + box package set version=@build.version@+@build.number@ + # master or snapshot + echo "Github Ref is $GITHUB_REF" + echo "BRANCH=master" >> $GITHUB_ENV + if [ $GITHUB_REF == 'refs/heads/development' ] + then + echo "BRANCH=development" >> $GITHUB_ENV + fi + + - name: Build ${{ env.MODULE_ID }} + run: | + box install commandbox-docbox + box task run taskfile=build/Build target=run :version=${{ env.VERSION }} :projectName=${{ env.MODULE_ID }} :buildID=${{ github.run_number }} :branch=${{ env.BRANCH }} + + - name: Upload Build Artifacts + if: success() + uses: actions/upload-artifact@v2 + with: + name: ${{ env.MODULE_ID }} + path: | + .artifacts/**/* + + - name: Upload Binaries to S3 + uses: jakejarvis/s3-sync-action@master + with: + args: --acl public-read + env: + AWS_S3_BUCKET: "downloads.ortussolutions.com" + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_ACCESS_SECRET }} + SOURCE_DIR: ".artifacts/${{ env.MODULE_ID }}" + DEST_DIR: "ortussolutions/coldbox-modules/${{ env.MODULE_ID }}" + + - name: Upload API Docs to S3 + uses: jakejarvis/s3-sync-action@master + with: + args: --acl public-read + env: + AWS_S3_BUCKET: "apidocs.ortussolutions.com" + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_ACCESS_SECRET }} + SOURCE_DIR: ".tmp/apidocs" + DEST_DIR: "coldbox-modules/${{ env.MODULE_ID }}/${{ env.VERSION }}" + + - name: Publish To ForgeBox + run: | + cd .tmp/${{ env.MODULE_ID }} + cat box.json + box forgebox publish + + - name: Inform Slack + if: ${{ always() }} + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_CHANNEL: coding + SLACK_COLOR: ${{ job.status }} # or a specific color like 'green' or '#ff00ff' + SLACK_ICON_EMOJI: ":bell:" + SLACK_MESSAGE: '${{ env.MODULE_ID }} Built with ${{ job.status }}!' + SLACK_TITLE: "${{ env.MODULE_ID }} Build" + SLACK_USERNAME: CI + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..8a63900 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,118 @@ +name: Pull Requests + +on: + push: + branches-ignore: + - "main" + - "master" + - "development" + pull_request: + branches: + - development + +jobs: + tests: + name: Tests + runs-on: ubuntu-20.04 + env: + DB_USER: root + DB_PASSWORD: root + strategy: + fail-fast: true + matrix: + cfengine: [ "lucee@5", "adobe@2016", "adobe@2018", "adobe@2021" ] + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Setup Database and Fixtures + run: | + sudo systemctl start mysql.service + mysql -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} -e 'CREATE DATABASE mementifier;' + + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "11" + + - name: Setup CommandBox + uses: elpete/setup-commandbox@v1.0.0 + + - name: Setup Environment For Testing Process + working-directory: ./test-harness + run: | + # Setup .env + touch .env + # ENV + printf "DB_HOST=localhost\n" >> .env + printf "DB_USER=${{ env.DB_USER }}\n" >> .env + printf "DB_PASSWORD=${{ env.DB_PASSWORD }}\n" >> .env + printf "DB_CLASS=com.mysql.cj.jdbc.Driver\n" >> .env + printf "DB_BUNDLEVERSION=8.0.19\n" >> .env + printf "DB_BUNDLENAME=com.mysql.cj\n" >> .env + + - name: Install Test Harness Dependencies + working-directory: ./test-harness + run: | + box install + + - name: Start ${{ matrix.cfengine }} Server + working-directory: ./test-harness + run: | + box server start serverConfigFile="server-${{ matrix.cfengine }}.json" --noSaveSettings --debug + # Install Adobe 2021 cfpm modules + if [[ "${{ matrix.cfengine }}" == "adobe@2021" ]] ; then + box run-script install:2021 + fi + curl http://127.0.0.1:60299 + + - name: Run Tests + working-directory: ./test-harness + run: | + mkdir tests/results + box package set testbox.runner="http://localhost:60299/tests/runner.cfm" + box testbox run --verbose outputFile=tests/results/test-results outputFormats=json,antjunit + + - name: Publish PR Test Reports + uses: mikepenz/action-junit-report@v2 + with: + report_paths: 'test-harness/tests/results/**/*.xml' + check_name: "${{ matrix.cfengine }} Test Results" + summary: true + + - name: Failure Debugging Info + if: ${{ failure() }} + working-directory: ./test-harness + run: | + box server log serverConfigFile="server-${{ matrix.cfengine }}.json" + if [[ "${{ matrix.cfengine }}" == "lucee-hibernate@5" ]] ; then + box cat "`box server info name=cborm-${{ matrix.cfengine }} property=serverHomeDirectory`/WEB-INF/lucee-server/context/logs/deploy.log" + fi + + format: + name: Format + runs-on: ubuntu-20.04 + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "11" + + - name: Set Up CommandBox + uses: elpete/setup-commandbox@v1.0.0 + + - name: Install CFFormat + run: box install commandbox-cfformat + + - name: Run CFFormat + run: box run-script format + + - name: Commit Format Changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Apply cfformat changes diff --git a/box.json b/box.json index 27107b9..75e93c7 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"Mementifier : The State Maker!", - "version":"2.7.0", + "version":"2.8.0", "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox-modules/mementifier/@build.version@/mementifier-@build.version@.zip", "author":"Ortus Solutions, Corp", "homepage":"https://github.com/coldbox-modules/mementifier", @@ -31,8 +31,8 @@ ], "scripts":{ "release":"recipe build/release.boxr", - "format":"cfformat run interceptors/,models/,test-harness/handlers,test-harness/tests/specs", - "format:watch":"cfformat watch interceptors/,models/,test-harness/handlers,test-harness/tests/specs ./.cfformat.json", - "format:check":"cfformat check interceptors/,models/,test-harness/handlers,test-harness/tests/specs" + "format":"cfformat run interceptors/,models/,test-harness/**/*.cfc ./.cfformat.json --overwrite", + "format:watch":"cfformat watch interceptors/,models/,test-harness/**/*.cfc ./.cfformat.json", + "format:check":"cfformat check interceptors/,models/,test-harness/**/*.cfc ./.cfformat.json" } -} \ No newline at end of file +} diff --git a/.travis.yml b/build/.travis.yml similarity index 100% rename from .travis.yml rename to build/.travis.yml diff --git a/build/Build.cfc b/build/Build.cfc index cf74dff..ae94650 100644 --- a/build/Build.cfc +++ b/build/Build.cfc @@ -2,202 +2,228 @@ * Build process for ColdBox Modules * Adapt to your needs. */ -component{ - - /** - * Constructor - */ - function init(){ - // Setup Pathing - variables.cwd = getCWD().reReplace( "\.$", "" ); - variables.artifactsDir = cwd & "/.artifacts"; - variables.buildDir = cwd & "/.tmp"; - variables.apiDocsURL = "http://localhost:60299/apidocs/"; - variables.testRunner = "http://localhost:60299/tests/runner.cfm"; - - // Source Excludes Not Added to final binary - variables.excludes = [ - ".gitignore", - ".travis.yml", - ".artifacts", - ".tmp", - "build", - "test-harness", - ".DS_Store", - ".git" - ]; - - // Cleanup + Init Build Directories - [ variables.buildDir, variables.artifactsDir ].each( function( item ){ - if( directoryExists( item ) ){ - directoryDelete( item, true ); - } - // Create directories - directoryCreate( item, true, true ); +component { + + /** + * Constructor + */ + function init(){ + // Setup Pathing + variables.cwd = getCWD().reReplace( "\.$", "" ); + variables.artifactsDir = cwd & "/.artifacts"; + variables.buildDir = cwd & "/.tmp"; + variables.apiDocsURL = "http://localhost:60299/apidocs/"; + variables.testRunner = "http://localhost:60299/tests/runner.cfm"; + + // Source Excludes Not Added to final binary + variables.excludes = [ + "build", + "node-modules", + "resources", + "test-harness", + "(package|package-lock).json", + "webpack.config.js", + "^\..*" + ]; + + // Cleanup + Init Build Directories + [ + variables.buildDir, + variables.artifactsDir + ].each( function( item ){ + if ( directoryExists( item ) ) { + directoryDelete( item, true ); + } + // Create directories + directoryCreate( item, true, true ); } ); // Create Mappings - fileSystemUtil.createMapping( "coldbox", variables.cwd & "test-harness/coldbox" ); - - return this; - } - - /** - * Run the build process: test, build source, docs, checksums - * - * @projectName The project name used for resources and slugs - * @version The version you are building - * @buldID The build identifier - * @branch The branch you are building - */ - function run( - required projectName, - version="1.0.0", - buildID=createUUID(), - branch="development" - ){ + fileSystemUtil.createMapping( + "coldbox", + variables.cwd & "test-harness/coldbox" + ); + + return this; + } + + /** + * Run the build process: test, build source, docs, checksums + * + * @projectName The project name used for resources and slugs + * @version The version you are building + * @buldID The build identifier + * @branch The branch you are building + */ + function run( + required projectName, + version = "1.0.0", + buildID = createUUID(), + branch = "development" + ){ // Create project mapping fileSystemUtil.createMapping( arguments.projectName, variables.cwd ); - // Run the tests - runTests(); + // Build the source + buildSource( argumentCollection = arguments ); - // Build the source - buildSource( argumentCollection=arguments ); + // Build Docs + arguments.outputDir = variables.buildDir & "/apidocs"; + docs( argumentCollection = arguments ); - // Build Docs - arguments.outputDir = variables.buildDir & "/apidocs"; - docs( argumentCollection=arguments ); - - // checksums + // checksums buildChecksums(); // Build latest changelog latestChangelog(); - // Finalize Message - print.line() - .boldMagentaLine( "Build Process is done! Enjoy your build!" ) - .toConsole(); - } - - /** - * Run the test suites - */ - function runTests(){ - // Tests First, if they fail then exit - print.blueLine( "Testing the package, please wait..." ).toConsole(); - - command( 'testbox run' ) - .params( - runner = variables.testRunner, - verbose = true, - outputFile = "build/results.json" - ) - .run(); - - // Check Exit Code? - if( shell.getExitCode() ){ - return error( "Cannot continue building, tests failed!" ); - } - } - - /** - * Build the source + // Finalize Message + print + .line() + .boldMagentaLine( "Build Process is done! Enjoy your build!" ) + .toConsole(); + } + + /** + * Run the test suites + */ + function runTests(){ + // Tests First, if they fail then exit + print.blueLine( "Testing the package, please wait..." ).toConsole(); + + command( "testbox run" ) + .params( + runner = variables.testRunner, + verbose = true, + outputFile = "#variables.cwd#/test-harness/results/test-results", + outputFormats="json,antjunit" + ) + .run(); + + // Check Exit Code? + if ( shell.getExitCode() ) { + return error( "Cannot continue building, tests failed!" ); + } + } + + /** + * Build the source * * @projectName The project name used for resources and slugs - * @version The version you are building - * @buldID The build identifier - * @branch The branch you are building - */ - function buildSource( - required projectName, - version="1.0.0", - buildID=createUUID(), - branch="development" - ){ - // Build Notice ID - print.line() - .boldMagentaLine( "Building #arguments.projectName# v#arguments.version#+#arguments.buildID# from #cwd# using the #arguments.branch# branch." ) - .toConsole(); - - // Prepare exports directory - variables.exportsDir = variables.artifactsDir & "/#projectName#/#arguments.version#"; - directoryCreate( variables.exportsDir, true, true ); - - // Project Build Dir - variables.projectBuildDir = variables.buildDir & "/#projectName#"; - directoryCreate( variables.projectBuildDir, true, true ); - - // Copy source - print.blueLine( "Copying source to build folder..." ).toConsole(); - copy( variables.cwd, variables.projectBuildDir ); - - // Create build ID - fileWrite( "#variables.projectBuildDir#/#projectName#-#version#+#buildID#", "Built with love on #dateTimeFormat( now(), "full")#" ); - - // Updating Placeholders - print.greenLine( "Updating version identifier to #arguments.version#" ).toConsole(); - command( 'tokenReplace' ) - .params( - path = "/#variables.projectBuildDir#/**", - token = "@build.version@", - replacement = arguments.version - ) - .run(); - - print.greenLine( "Updating build identifier to #arguments.buildID#" ).toConsole(); - command( 'tokenReplace' ) - .params( - path = "/#variables.projectBuildDir#/**", - token = ( arguments.branch == "master" ? "@build.number@" : "+@build.number@" ), - replacement = ( arguments.branch == "master" ? arguments.buildID : "-snapshot" ) - ) - .run(); - - // zip up source - var destination = "#variables.exportsDir#/#projectName#-#version#.zip"; - print.greenLine( "Zipping code to #destination#" ).toConsole(); - cfzip( - action="zip", - file="#destination#", - source="#variables.projectBuildDir#", - overwrite=true, - recurse=true - ); - - // Copy box.json for convenience - fileCopy( "#variables.projectBuildDir#/box.json", variables.exportsDir ); - } - - /** - * Produce the API Docs - */ - function docs( required projectName, version="1.0.0", outputDir=".tmp/apidocs" ){ - // Generate Docs - print.greenLine( "Generating API Docs, please wait..." ).toConsole(); - directoryCreate( arguments.outputDir, true, true ); - - command( 'docbox generate' ) - .params( - "source" = "models", - "mapping" = "models", - "strategy-projectTitle" = "#arguments.projectName# v#arguments.version#", - "strategy-outputDir" = arguments.outputDir - ) - .run(); - - print.greenLine( "API Docs produced at #arguments.outputDir#" ).toConsole(); - - var destination = "#variables.exportsDir#/#projectName#-docs-#version#.zip"; - print.greenLine( "Zipping apidocs to #destination#" ).toConsole(); - cfzip( - action="zip", - file="#destination#", - source="#arguments.outputDir#", - overwrite=true, - recurse=true - ); + * @version The version you are building + * @buldID The build identifier + * @branch The branch you are building + */ + function buildSource( + required projectName, + version = "1.0.0", + buildID = createUUID(), + branch = "development" + ){ + // Build Notice ID + print + .line() + .boldMagentaLine( + "Building #arguments.projectName# v#arguments.version#+#arguments.buildID# from #cwd# using the #arguments.branch# branch." + ) + .toConsole(); + + // Prepare exports directory + variables.exportsDir = variables.artifactsDir & "/#projectName#/#arguments.version#"; + directoryCreate( variables.exportsDir, true, true ); + + // Project Build Dir + variables.projectBuildDir = variables.buildDir & "/#projectName#"; + directoryCreate( + variables.projectBuildDir, + true, + true + ); + + // Copy source + print.blueLine( "Copying source to build folder..." ).toConsole(); + copy( + variables.cwd, + variables.projectBuildDir + ); + + // Create build ID + fileWrite( + "#variables.projectBuildDir#/#projectName#-#version#+#buildID#", + "Built with love on #dateTimeFormat( now(), "full" )#" + ); + + // Updating Placeholders + print.greenLine( "Updating version identifier to #arguments.version#" ).toConsole(); + command( "tokenReplace" ) + .params( + path = "/#variables.projectBuildDir#/**", + token = "@build.version@", + replacement = arguments.version + ) + .run(); + + print.greenLine( "Updating build identifier to #arguments.buildID#" ).toConsole(); + command( "tokenReplace" ) + .params( + path = "/#variables.projectBuildDir#/**", + token = ( arguments.branch == "master" ? "@build.number@" : "+@build.number@" ), + replacement = ( arguments.branch == "master" ? arguments.buildID : "-snapshot" ) + ) + .run(); + + // zip up source + var destination = "#variables.exportsDir#/#projectName#-#version#.zip"; + print.greenLine( "Zipping code to #destination#" ).toConsole(); + cfzip( + action = "zip", + file = "#destination#", + source = "#variables.projectBuildDir#", + overwrite = true, + recurse = true + ); + + // Copy box.json for convenience + fileCopy( + "#variables.projectBuildDir#/box.json", + variables.exportsDir + ); + } + + /** + * Produce the API Docs + */ + function docs( + required projectName, + version = "1.0.0", + outputDir = ".tmp/apidocs" + ){ + // Create project mapping + fileSystemUtil.createMapping( arguments.projectName, variables.cwd ); + // Generate Docs + print.greenLine( "Generating API Docs, please wait..." ).toConsole(); + directoryCreate( arguments.outputDir, true, true ); + + command( "docbox generate" ) + .params( + "source" = "models", + "mapping" = "models", + "strategy-projectTitle" = "#arguments.projectName# v#arguments.version#", + "strategy-outputDir" = arguments.outputDir + ) + .run(); + + print.greenLine( "API Docs produced at #arguments.outputDir#" ).toConsole(); + + var destination = "#variables.exportsDir#/#projectName#-docs-#version#.zip"; + print.greenLine( "Zipping apidocs to #destination#" ).toConsole(); + cfzip( + action = "zip", + file = "#destination#", + source = "#arguments.outputDir#", + overwrite = true, + recurse = true + ); } /** @@ -206,13 +232,13 @@ component{ function latestChangelog(){ print.blueLine( "Building latest changelog..." ).toConsole(); - if( !fileExists( variables.cwd & "changelog.md" ) ){ + if ( !fileExists( variables.cwd & "changelog.md" ) ) { return error( "Cannot continue building, changelog.md file doesn't exist!" ); } fileWrite( variables.cwd & "changelog-latest.md", - fileRead( variables.cwd & 'changelog.md' ).split( '----' )[2].trim() & chr( 13 ) & chr( 10 ) + fileRead( variables.cwd & "changelog.md" ).split( "----" )[ 2 ].trim() & chr( 13 ) & chr( 10 ) ); print @@ -221,52 +247,70 @@ component{ .line( fileRead( variables.cwd & "changelog-latest.md" ) ); } - /********************************************* PRIVATE HELPERS *********************************************/ - - /** - * Build Checksums - */ - private function buildChecksums(){ - print.greenLine( "Building checksums" ).toConsole(); - command( 'checksum' ) - .params( path = '#variables.exportsDir#/*.zip', algorithm = 'SHA-512', extension="sha512", write=true ) - .run(); - command( 'checksum' ) - .params( path = '#variables.exportsDir#/*.zip', algorithm = 'md5', extension="md5", write=true ) - .run(); - } - - /** - * DirectoryCopy is broken in lucee - */ - private function copy( src, target, recurse=true ){ - // process paths with excludes - directoryList( src, false, "path", function( path ){ - var isExcluded = false; - variables.excludes.each( function( item ){ - if( path.replaceNoCase( variables.cwd, "", "all" ).findNoCase( item ) ){ - isExcluded = true; - } - } ); - return !isExcluded; - }).each( function( item ){ - // Copy to target - if( fileExists( item ) ){ - print.blueLine( "Copying #item#" ).toConsole(); - fileCopy( item, target ); - } else { - print.greenLine( "Copying directory #item#" ).toConsole(); - directoryCopy( item, target & "/" & item.replace( src, "" ), true ); - } - } ); - } - - /** + /********************************************* PRIVATE HELPERS *********************************************/ + + /** + * Build Checksums + */ + private function buildChecksums(){ + print.greenLine( "Building checksums" ).toConsole(); + command( "checksum" ) + .params( + path = "#variables.exportsDir#/*.zip", + algorithm = "SHA-512", + extension = "sha512", + write = true + ) + .run(); + command( "checksum" ) + .params( + path = "#variables.exportsDir#/*.zip", + algorithm = "md5", + extension = "md5", + write = true + ) + .run(); + } + + /** + * DirectoryCopy is broken in lucee + */ + private function copy( src, target, recurse = true ){ + // process paths with excludes + directoryList( + src, + false, + "path", + function( path ){ + var isExcluded = false; + variables.excludes.each( function( item ){ + if ( path.replaceNoCase( variables.cwd, "", "all" ).reFindNoCase( item ) ) { + isExcluded = true; + } + } ); + return !isExcluded; + } + ).each( function( item ){ + // Copy to target + if ( fileExists( item ) ) { + print.blueLine( "Copying #item#" ).toConsole(); + fileCopy( item, target ); + } else { + print.greenLine( "Copying directory #item#" ).toConsole(); + directoryCopy( + item, + target & "/" & item.replace( src, "" ), + true + ); + } + } ); + } + + /** * Gets the last Exit code to be used **/ - private function getExitCode() { - return (createObject( 'java', 'java.lang.System' ).getProperty( 'cfml.cli.exitCode' ) ?: 0); - + private function getExitCode(){ + return ( createObject( "java", "java.lang.System" ).getProperty( "cfml.cli.exitCode" ) ?: 0 ); } -} \ No newline at end of file +} diff --git a/build/release.boxr b/build/release.boxr index e216f22..d0ba4f7 100755 --- a/build/release.boxr +++ b/build/release.boxr @@ -22,4 +22,4 @@ # Bump to prepare for a new release, do minor, change if needed and don't tag bump --minor --!tagVersion !git commit -a -m "version bump" -!git push origin development \ No newline at end of file +!git push origin development diff --git a/changelog.md b/changelog.md index 3e37f7c..436a985 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ---- +## [2.8.0] => 2022-JAN-11 + +### Added + +* Migration to github actions +* CFFormatting Rules + +### Fixed + +* Composite keys and no default includes fails (https://github.com/coldbox-modules/mementifier/pull/24) +* Fix mappers for keys not in memento (https://github.com/coldbox-modules/mementifier/pull/23) + +---- + ## [2.7.0] => 2021-JUN-07 ### Added @@ -59,7 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * Thanks to @elpete you can now add date/time formatting rules at the `getmemento()` level and the `this.memento` level. Please see the [readme](readme.md) for further information. - + ---- ## [2.2.1] => 2020-NOV-06 @@ -67,7 +81,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed * Reverted missing `nestedIncludes.len()` for ignore defaults on nested hierarchies. - + ---- ## [2.2.0] => 2020-NOV-05 @@ -186,4 +200,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.0.0] -* First iteration of this module \ No newline at end of file +* First iteration of this module diff --git a/interceptors/Mementifier.cfc b/interceptors/Mementifier.cfc index 0b8a435..953703f 100644 --- a/interceptors/Mementifier.cfc +++ b/interceptors/Mementifier.cfc @@ -76,26 +76,18 @@ component { } // Inject helper methods - arguments.entity.$injectMixin( - "$buildNestedMementoList", - variables.$buildNestedMementoList - ); - arguments.entity.$injectMixin( - "$buildNestedMementoStruct", - variables.$buildNestedMementoStruct - ); + arguments.entity.$injectMixin( "$buildNestedMementoList", variables.$buildNestedMementoList ); + arguments.entity.$injectMixin( "$buildNestedMementoStruct", variables.$buildNestedMementoStruct ); arguments.entity.$injectMixin( "$getDeepProperties", variables.$getDeepProperties ); // We do simple date formatters as they are faster than CFML methods var dateMask = isNull( this.memento.dateMask ) ? variables.settings.dateMask : this.memento.dateMask; var timeMask = isNull( this.memento.timeMask ) ? variables.settings.timeMask : this.memento.timeMask; - arguments.entity.$FORMATTER_ISO8601 = createObject( - "java", - "java.text.SimpleDateFormat" - ).init( "yyyy-MM-dd'T'HH:mm:ssXXX" ); - arguments.entity.$FORMATTER_CUSTOM = createObject( - "java", - "java.text.SimpleDateFormat" - ).init( "#dateMask# #timeMask#" ); + arguments.entity.$FORMATTER_ISO8601 = createObject( "java", "java.text.SimpleDateFormat" ).init( + "yyyy-MM-dd'T'HH:mm:ssXXX" + ); + arguments.entity.$FORMATTER_CUSTOM = createObject( "java", "java.text.SimpleDateFormat" ).init( + "#dateMask# #timeMask#" + ); // Do we set timezones? if ( len( variables.settings.convertToTimezone ) ) { var tz = createObject( "java", "java.util.TimeZone" ).getTimeZone( @@ -110,16 +102,16 @@ component { /** * Construct a memento representation from an entity according to includes and exclude lists * - * @includes The properties array or list to build the memento with alongside the default includes - * @excludes The properties array or list to exclude from the memento alongside the default excludes - * @mappers A struct of key-function pairs that will map properties to closures/lambadas to process the item value. The closure will transform the item value. - * @defaults A struct of key-value pairs that denotes the default values for properties if they are null, defaults for everything are a blank string. + * @includes The properties array or list to build the memento with alongside the default includes + * @excludes The properties array or list to exclude from the memento alongside the default excludes + * @mappers A struct of key-function pairs that will map properties to closures/lambadas to process the item value. The closure will transform the item value. + * @defaults A struct of key-value pairs that denotes the default values for properties if they are null, defaults for everything are a blank string. * @ignoreDefaults If set to true, default includes and excludes will be ignored and only the incoming `includes` and `excludes` list will be used. * @trustedGetters If set to true, getters will not be checked for in the `this` scope before trying to invoke them. - * @iso8601Format If set to true, will use the ISO 8601 standard for formatting dates - * @dateMask The date mask to use when formatting datetimes. Only used if iso8601Format is false. - * @timeMask The time mask to use when formatting datetimes. Only used if iso8601Format is false. - * @profile The profile to use instead of the defaults + * @iso8601Format If set to true, will use the ISO 8601 standard for formatting dates + * @dateMask The date mask to use when formatting datetimes. Only used if iso8601Format is false. + * @timeMask The time mask to use when formatting datetimes. Only used if iso8601Format is false. + * @profile The profile to use instead of the defaults */ struct function getMemento( includes = "", @@ -222,7 +214,7 @@ component { // Append primary keys if ( entityMd.hasIdentifierProperty() ) { arrayAppend( thisMemento.defaultIncludes, entityMd.getIdentifierPropertyName() ); - } else if ( thisMemento.defaultIncludes.getIdentifierType().isComponentType() ) { + } else if ( entityMd.getIdentifierType().isComponentType() ) { arrayAppend( thisMemento.defaultIncludes, listToArray( arrayToList( entityMd.getIdentifierType().getPropertyNames() ) ), @@ -309,16 +301,14 @@ component { } } // If the key doesn't exist and there is no mapper for the item, go to the next item. - } else if ( !structKeyExists( arguments.mappers, item ) ) { + } else if ( !structKeyExists( thisMemento.mappers, item ) ) { continue; } // Verify Nullness thisValue = isNull( thisValue ) ? ( arrayContainsNoCase( thisMemento.defaults.keyArray(), item ) ? ( - isNull( thisMemento.defaults[ item ] ) ? javacast( "null", "" ) : thisMemento.defaults[ - item - ] + isNull( thisMemento.defaults[ item ] ) ? javacast( "null", "" ) : thisMemento.defaults[ item ] ) : variables.$mementifierSettings.nullDefaultValue ) : thisValue; @@ -341,9 +331,7 @@ component { // Iso Date? if ( arguments.iso8601Format ) { // we need to convert trailing Zulu time designations offset or JS libs like Moment will not know how to parse it - result[ thisAlias ] = this.$FORMATTER_ISO8601 - .format( thisValue ) - .replace( "Z", "+00:00" ); + result[ thisAlias ] = this.$FORMATTER_ISO8601.format( thisValue ).replace( "Z", "+00:00" ); } else { result[ thisAlias ] = customDateFormatter.format( thisValue ); } @@ -436,10 +424,9 @@ component { result[ item ] = thisMapper( result[ item ], result ); } else { // Check for null values - result[ item ] = ( !result.keyExists( item ) || isNull( result[ item ] ) ) ? javacast( - "null", - "" - ) : result[ item ]; + result[ item ] = ( !result.keyExists( item ) || isNull( result[ item ] ) ) ? javacast( "null", "" ) : result[ + item + ]; } } @@ -458,10 +445,7 @@ component { function $buildNestedMementoList( required list, required root ){ return arguments.list .filter( function( target ){ - return listFirst( arguments.target, "." ) == root && listLen( - arguments.target, - "." - ) > 1; + return listFirst( arguments.target, "." ) == root && listLen( arguments.target, "." ) > 1; } ) .map( function( target ){ return listDeleteAt( arguments.target, 1, "." ); @@ -480,7 +464,7 @@ component { * Build a new memento mappers/defaults struct using the target list and a property root * * @struct The struct to use for construction - * @root The root to filter out + * @root The root to filter out * * @return A struct of the new hiearchy to use */ diff --git a/models/ResultsMapper.cfc b/models/ResultsMapper.cfc index c9162e8..9735000 100644 --- a/models/ResultsMapper.cfc +++ b/models/ResultsMapper.cfc @@ -4,10 +4,10 @@ * *
* data = { - * "results" = [], - * "resultsMap" = { + * "results" = [], + * "resultsMap" = { * - * } + * } * } **/ @@ -24,12 +24,12 @@ component singleton { * Construct a memento representation using a results map. This process will iterate over the collection and create a * results array with all the identifiers and a struct keyed by identifier of the mememnto data. * - * @collection The target collection - * @id The identifier key, defaults to `id` for simplicity. - * @includes The properties array or list to build the memento with alongside the default includes - * @excludes The properties array or list to exclude from the memento alongside the default excludes - * @mappers A struct of key-function pairs that will map properties to closures/lambadas to process the item value. The closure will transform the item value. - * @defaults A struct of key-value pairs that denotes the default values for properties if they are null, defaults for everything are a blank string. + * @collection The target collection + * @id The identifier key, defaults to `id` for simplicity. + * @includes The properties array or list to build the memento with alongside the default includes + * @excludes The properties array or list to exclude from the memento alongside the default excludes + * @mappers A struct of key-function pairs that will map properties to closures/lambadas to process the item value. The closure will transform the item value. + * @defaults A struct of key-value pairs that denotes the default values for properties if they are null, defaults for everything are a blank string. * @ignoreDefaults If set to true, default includes and excludes will be ignored and only the incoming `includes` and `excludes` list will be used. * @trustedGetters If set to true, getters will not be checked for in the `this` scope before trying to invoke them. * diff --git a/test-harness/.cfconfig.json b/test-harness/.cfconfig.json index 131e366..da33319 100644 --- a/test-harness/.cfconfig.json +++ b/test-harness/.cfconfig.json @@ -12,19 +12,21 @@ "allowSelect":true, "allowUpdate":true, "blob":"false", - "class":"com.mysql.jdbc.Driver", + "bundleName": "${DB_BUNDLENAME}", + "bundleVersion": "${DB_BUNDLEVERSION}", + "class":"${DB_CLASS}", "clob":"false", "connectionTimeout":"1", - "custom":"useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=true&useSSL=false", + "custom":"useUnicode=true&characterEncoding=UTF8&serverTimezone=America%2FChicago&useLegacyDatetimeCode=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true", "database":"mementifier", "dbdriver":"MySQL", "dsn":"jdbc:mysql://{host}:{port}/{database}", - "host":"${DB_HOST}", + "host":"${DB_HOST:127.0.0.1}", "metaCacheTimeout":"60000", - "password":"${DB_PASSWORD}", - "port":"3306", + "password": "${DB_PASSWORD}", + "port": "${DB_PORT:3306}", "storage":"false", - "username":"${DB_USERNAME}", + "username": "${DB_USER:root}", "validate":"false" } }, @@ -35,4 +37,4 @@ "requestTimeout":"0,0,0,90", "robustExceptionEnabled":true, "whitespaceManagement":"white-space-pref" -} \ No newline at end of file +} diff --git a/test-harness/.cflintrc b/test-harness/.cflintrc deleted file mode 100644 index 9e26dfe..0000000 --- a/test-harness/.cflintrc +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/test-harness/Application.cfc b/test-harness/Application.cfc index f433f56..7b33000 100644 --- a/test-harness/Application.cfc +++ b/test-harness/Application.cfc @@ -1,80 +1,102 @@ /** -******************************************************************************** -Copyright 2005-2007 ColdBox Framework by Luis Majano and Ortus Solutions, Corp -www.ortussolutions.com -******************************************************************************** -*/ -component{ + * ******************************************************************************* + * ******************************************************************************* + */ +component { // UPDATE THE NAME OF THE MODULE IN TESTING BELOW request.MODULE_NAME = "mementifier"; + request.MODULE_PATH = "mementifier"; // Application properties this.name = hash( getCurrentTemplatePath() ); this.sessionManagement = true; - this.sessionTimeout = createTimeSpan(0,0,15,0); - this.setClientCookies = true; + this.sessionTimeout = createTimespan( 0, 0, 15, 0 ); + this.setClientCookies = true; - /************************************** - LUCEE Specific Settings - **************************************/ + /************************************** + **************************************/ // buffer the output of a tag/function body to output in case of a exception - this.bufferOutput = true; + this.bufferOutput = true; // Activate Gzip Compression - this.compression = false; + this.compression = false; // Turn on/off white space managemetn - this.whiteSpaceManagement = "smart"; + this.whiteSpaceManagement = "smart"; // Turn on/off remote cfc content whitespace this.suppressRemoteComponentContent = false; // COLDBOX STATIC PROPERTY, DO NOT CHANGE UNLESS THIS IS NOT THE ROOT OF YOUR COLDBOX APP - COLDBOX_APP_ROOT_PATH = getDirectoryFromPath( getCurrentTemplatePath() ); + COLDBOX_APP_ROOT_PATH = getDirectoryFromPath( getCurrentTemplatePath() ); // The web server mapping to this application. Used for remote purposes or static purposes - COLDBOX_APP_MAPPING = ""; + COLDBOX_APP_MAPPING = ""; // COLDBOX PROPERTIES - COLDBOX_CONFIG_FILE = ""; + COLDBOX_CONFIG_FILE = ""; // COLDBOX APPLICATION KEY OVERRIDE - COLDBOX_APP_KEY = ""; + COLDBOX_APP_KEY = ""; - // Mappings - this.mappings[ "/root" ] = COLDBOX_APP_ROOT_PATH; + // Mappings + this.mappings[ "/root" ] = COLDBOX_APP_ROOT_PATH; this.mappings[ "/cborm" ] = COLDBOX_APP_ROOT_PATH & "/modules/cborm"; // Map back to its root - moduleRootPath = REReplaceNoCase( this.mappings[ "/root" ], "#request.MODULE_NAME#(\\|/)test-harness(\\|/)", "" ); - modulePath = REReplaceNoCase( this.mappings[ "/root" ], "test-harness(\\|/)", "" ); + moduleRootPath = reReplaceNoCase( + this.mappings[ "/root" ], + "#request.MODULE_NAME#(\\|/)test-harness(\\|/)", + "" + ); + modulePath = reReplaceNoCase( + this.mappings[ "/root" ], + "test-harness(\\|/)", + "" + ); // Module Root + Path Mappings - this.mappings[ "/moduleroot" ] = moduleRootPath; + this.mappings[ "/moduleroot" ] = moduleRootPath; this.mappings[ "/#request.MODULE_NAME#" ] = modulePath; // ORM definitions: ENABLE IF NEEDED this.datasource = "mementifier"; this.ormEnabled = "true"; + this.ormSettings = { - cfclocation = [ "models" ], - logSQL = true, - dbcreate = "dropcreate", - flushAtRequestEnd = false, - eventhandling = true, - eventHandler = "cborm.models.EventHandler", - skipcfcWithError = false + cfclocation : [ "models" ], + logSQL : true, + dbcreate : "update", + dialect : "org.hibernate.dialect.MySQL5InnoDBDialect", + secondarycacheenabled : false, + cacheProvider : "ehcache", + automanageSession : false, + flushAtRequestEnd : false, + eventhandling : true, + eventHandler : "cborm.models.EventHandler", + skipcfcWithError : false }; - //applicationstop();abort; - //ormReload(); + // applicationstop();abort; + // ormReload(); // application start public boolean function onApplicationStart(){ - application.cbBootstrap = new coldbox.system.Bootstrap( COLDBOX_CONFIG_FILE, COLDBOX_APP_ROOT_PATH, COLDBOX_APP_KEY, COLDBOX_APP_MAPPING ); + application.cbBootstrap = new coldbox.system.Bootstrap( + COLDBOX_CONFIG_FILE, + COLDBOX_APP_ROOT_PATH, + COLDBOX_APP_KEY, + COLDBOX_APP_MAPPING + ); application.cbBootstrap.loadColdbox(); return true; } // request start - public boolean function onRequestStart(String targetPage){ + public boolean function onRequestStart( String targetPage ){ + if ( !structKeyExists( application, "cbBootstrap" ) ) { + onApplicationStart(); + } - if( structKeyExists( url, "fwreinit" ) ){ + if ( url.keyExists( "fwreinit" ) ) { + if ( server.keyExists( "lucee" ) ) { + pagePoolClear(); + } ormReload(); } @@ -89,11 +111,11 @@ component{ } public void function onSessionEnd( struct sessionScope, struct appScope ){ - arguments.appScope.cbBootStrap.onSessionEnd( argumentCollection=arguments ); + arguments.appScope.cbBootStrap.onSessionEnd( argumentCollection = arguments ); } public boolean function onMissingTemplate( template ){ - return application.cbBootstrap.onMissingTemplate( argumentCollection=arguments ); + return application.cbBootstrap.onMissingTemplate( argumentCollection = arguments ); } -} \ No newline at end of file +} diff --git a/test-harness/box.json b/test-harness/box.json index 40587e0..8b6844d 100644 --- a/test-harness/box.json +++ b/test-harness/box.json @@ -9,8 +9,8 @@ "cborm":"^3.0.0" }, "devDependencies":{ - "testbox":"^4.0.0", - "mockdatacfc":"^3.1.0+13" + "testbox":"*", + "mockdatacfc":"*" }, "installPaths":{ "coldbox":"coldbox/", @@ -22,6 +22,10 @@ "runner":"http://localhost:60299/tests/runner.cfm" }, "scripts":{ - "cleanMementifier":"rm modules/cborm/modules/mementifier --force --recurse" + "postInstall":"run-script cleanMementifier && echo 'Mementifier Cleaned'", + "cleanMementifier":"rm modules/cborm/modules/mementifier --force --recurse", + "cfpm":"echo '\".engine/adobe2021/WEB-INF/cfusion/bin/cfpm.sh\"' | run", + "cfpm:install":"echo '\".engine/adobe2021/WEB-INF/cfusion/bin/cfpm.sh\" install ${1}' | run", + "install:2021":"run-script cfpm:install zip,debugger,orm,mysql,feed" } -} \ No newline at end of file +} diff --git a/test-harness/config/Coldbox.cfc b/test-harness/config/Coldbox.cfc index c24102e..061e363 100644 --- a/test-harness/config/Coldbox.cfc +++ b/test-harness/config/Coldbox.cfc @@ -56,11 +56,14 @@ logBox = { // Define Appenders appenders = { - files={class="coldbox.system.logging.appenders.RollingFileAppender", - properties = { - filename = "tester", filePath="/#appMapping#/logs" + files : { + class : "coldbox.system.logging.appenders.RollingFileAppender", + properties : { + filename : "tester", + filePath : "/#appMapping#/logs" } - } + }, + console : { class : "coldbox.system.logging.appenders.ConsoleAppender" } }, // Root Logger root = { levelmax="DEBUG", appenders="*" }, @@ -78,4 +81,4 @@ ); } -} \ No newline at end of file +} diff --git a/test-harness/config/Routes.cfm b/test-harness/config/Routes.cfm deleted file mode 100644 index a304d25..0000000 --- a/test-harness/config/Routes.cfm +++ /dev/null @@ -1,46 +0,0 @@ -