|
| 1 | +component { |
| 2 | + |
| 3 | + function init( required download ) { |
| 4 | + variables.download = arguments.download; |
| 5 | + return this; |
| 6 | + } |
| 7 | + |
| 8 | + /** |
| 9 | + * Process raw versions from the update provider and filter them for the changelog |
| 10 | + * @versions The raw versions struct from download.getVersions() |
| 11 | + * @returns Struct with { major: {}, latestSnapshots: {} } |
| 12 | + */ |
| 13 | + function processVersions( required struct versions ) localmode=true { |
| 14 | + var major = {}; |
| 15 | + var snapshots = {}; |
| 16 | + |
| 17 | + // add types to each version |
| 18 | + loop struct=arguments.versions index="local.vs" item="local.data" { |
| 19 | + if ( findNoCase( "-snapshot", data.version ) ) { |
| 20 | + data['type'] = "snapshots"; |
| 21 | + } else if ( findNoCase( "-rc", data.version ) ) { |
| 22 | + data['type'] = "rc"; |
| 23 | + } else if ( findNoCase( "-beta", data.version ) ) { |
| 24 | + data['type'] = "beta"; |
| 25 | + } else if ( findNoCase( "-alpha", data.version ) ) { |
| 26 | + data['type'] = "alpha"; |
| 27 | + } else { |
| 28 | + data['type'] = "releases"; |
| 29 | + } |
| 30 | + data['versionNoAppendix'] = data.version; |
| 31 | + |
| 32 | + if ( data.type != "snapshots" ) { |
| 33 | + major[ vs ] = data; |
| 34 | + } else { |
| 35 | + // Track all snapshots - key (vs) is already the sorted version |
| 36 | + // Extract major version from key: "07" from "07.000.001.0044.000" |
| 37 | + var majorVersion = listFirst( vs, "." ); |
| 38 | + |
| 39 | + // Keep only the latest snapshot per major version (highest key) |
| 40 | + if ( !structKeyExists( snapshots, majorVersion ) || vs > snapshots[ majorVersion ].key ) { |
| 41 | + snapshots[ majorVersion ] = { |
| 42 | + key: vs, |
| 43 | + data: data |
| 44 | + }; |
| 45 | + } |
| 46 | + } |
| 47 | + } |
| 48 | + |
| 49 | + // Promote the latest snapshot per major version to major |
| 50 | + // BUT only if there isn't already an RC/Beta/Release with the same version |
| 51 | + structEach( snapshots, function( majorVer, snapshot ) { |
| 52 | + var snapshotKey = snapshot.key; |
| 53 | + var snapshotData = snapshot.data; |
| 54 | + |
| 55 | + // Extract version prefix (first 4 parts) to check for duplicates |
| 56 | + // e.g., "07.000.001.0044" from "07.000.001.0044.000" |
| 57 | + var versionPrefix = listDeleteAt( snapshotKey, listLen( snapshotKey, "." ), "." ); |
| 58 | + |
| 59 | + // Check if there's already an RC/Beta/Release with same version |
| 60 | + var hasDuplicate = false; |
| 61 | + loop list=".050,.075,.100" index="local.qualifier" { |
| 62 | + if ( structKeyExists( major, versionPrefix & qualifier ) ) { |
| 63 | + hasDuplicate = true; |
| 64 | + break; |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + // If no duplicate, promote this snapshot to major |
| 69 | + if ( !hasDuplicate ) { |
| 70 | + major[ snapshotKey ] = snapshotData; |
| 71 | + } |
| 72 | + }); |
| 73 | + |
| 74 | + return { |
| 75 | + major: major, |
| 76 | + latestSnapshots: snapshots |
| 77 | + }; |
| 78 | + } |
| 79 | + |
| 80 | + /** |
| 81 | + * Get sorted array of version keys for display |
| 82 | + * @major The major versions struct |
| 83 | + * @returns Array of version keys, sorted newest first |
| 84 | + */ |
| 85 | + function getSortedVersions( required struct major ) localmode=true { |
| 86 | + return structKeyArray( arguments.major ).reverse().sort( "text", "desc" ); |
| 87 | + } |
| 88 | + |
| 89 | + /** |
| 90 | + * Sort changelog struct keys by version, filtering to only include versions matching the major version filter |
| 91 | + * @changelog The changelog struct from download.getChangelog() - keys are fix versions, values are ticket structs |
| 92 | + * @majorVersionFilter The major version to filter by (e.g., "7.0") |
| 93 | + * @returns Array of version keys, sorted newest first, filtered to only include matching major versions |
| 94 | + */ |
| 95 | + function getSortedChangelogVersions( required struct changelog, required string majorVersionFilter ) localmode=true { |
| 96 | + var versions = structKeyArray( arguments.changelog ); |
| 97 | + |
| 98 | + // Filter to only include versions that start with the major version filter |
| 99 | + var filtered = []; |
| 100 | + loop array=versions index="local.i" item="local.ver" { |
| 101 | + if ( left( ver, len( arguments.majorVersionFilter ) ) eq arguments.majorVersionFilter ) { |
| 102 | + arrayAppend( filtered, ver ); |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + // Sort using proper version sorting (convert to sortable format first) |
| 107 | + // Use callback to compare using toVersionSortable format |
| 108 | + arraySort( filtered, function( v1, v2 ) { |
| 109 | + try { |
| 110 | + var sorted1 = toVersionSortable( arguments.v1 ); |
| 111 | + var sorted2 = toVersionSortable( arguments.v2 ); |
| 112 | + // Descending order (newest first) |
| 113 | + return compare( sorted2, sorted1 ); |
| 114 | + } catch ( any e ) { |
| 115 | + // Fallback to text comparison if version parsing fails |
| 116 | + return compareNoCase( arguments.v2, arguments.v1 ); |
| 117 | + } |
| 118 | + }); |
| 119 | + |
| 120 | + return filtered; |
| 121 | + } |
| 122 | + |
| 123 | + /** |
| 124 | + * Build the changelog data array for a specific major version |
| 125 | + * @versions The processed versions struct (from processVersions) |
| 126 | + * @arrVersions Array of sorted version keys (from getSortedVersions) |
| 127 | + * @majorVersionFilter Major version to filter by (e.g., "7.0") |
| 128 | + * @returns Array of structs with version metadata and changelog data |
| 129 | + */ |
| 130 | + function buildChangelogData( required struct versions, required array arrVersions, required string majorVersionFilter ) localmode=true { |
| 131 | + var arrChangeLogs = []; |
| 132 | + |
| 133 | + loop array=arguments.arrVersions index="local.idx" item="local._version" { |
| 134 | + var version = arguments.versions[ _version ].version; |
| 135 | + var prevVersion = ""; |
| 136 | + |
| 137 | + // Determine previous version for changelog range |
| 138 | + if ( idx lt arrayLen( arguments.arrVersions ) ) { |
| 139 | + prevVersion = arguments.versions[ arguments.arrVersions[ idx + 1 ] ].version; |
| 140 | + } else { |
| 141 | + // Last version - use the oldest version from the sorted array (last item) |
| 142 | + var lastKey = arguments.arrVersions[ arrayLen( arguments.arrVersions ) ]; |
| 143 | + prevVersion = arguments.versions[ lastKey ].version; |
| 144 | + } |
| 145 | + |
| 146 | + // Determine header type and title |
| 147 | + var versionTitle = version; |
| 148 | + var header = "h4"; |
| 149 | + switch( arguments.versions[ _version ].type ) { |
| 150 | + case "releases": |
| 151 | + header = "h2"; |
| 152 | + versionTitle &= " Stable"; |
| 153 | + break; |
| 154 | + } |
| 155 | + |
| 156 | + // Fetch changelog only if version matches the major version filter |
| 157 | + var changelog = {}; |
| 158 | + var versionReleaseDate = ""; |
| 159 | + if ( left( version, len( arguments.majorVersionFilter ) ) eq arguments.majorVersionFilter ) { |
| 160 | + changelog = variables.download.getChangelog( prevVersion, version, false, true ); |
| 161 | + versionReleaseDate = variables.download.getReleaseDate( version ); |
| 162 | + } |
| 163 | + |
| 164 | + if ( !isStruct( changelog ) ) { |
| 165 | + changelog = {}; |
| 166 | + } |
| 167 | + |
| 168 | + arrayAppend( arrChangeLogs, { |
| 169 | + version: version, |
| 170 | + _version: _version, |
| 171 | + type: arguments.versions[ _version ].type, |
| 172 | + prevVersion: prevVersion, |
| 173 | + versionReleaseDate: versionReleaseDate, |
| 174 | + changelog: changelog, |
| 175 | + header: header, |
| 176 | + versionTitle: versionTitle |
| 177 | + }); |
| 178 | + } |
| 179 | + |
| 180 | + return arrChangeLogs; |
| 181 | + } |
| 182 | + |
| 183 | + /** |
| 184 | + * Convert a version string to sortable format (from VersionUtils.cfc logic) |
| 185 | + * @version Version string like "7.0.1.44-SNAPSHOT" |
| 186 | + * @returns Sortable version string like "07.000.001.0044.000" |
| 187 | + */ |
| 188 | + private function toVersionSortable( required string version ) localmode=true { |
| 189 | + var arr = listToArray( arguments.version, '.' ); |
| 190 | + |
| 191 | + if ( arr.len() != 4 || !isNumeric( arr[ 1 ] ) || !isNumeric( arr[ 2 ] ) || !isNumeric( arr[ 3 ] ) ) { |
| 192 | + throw "version number [" & arguments.version & "] is invalid"; |
| 193 | + } |
| 194 | + |
| 195 | + var sct = { |
| 196 | + major: arr[ 1 ] + 0, |
| 197 | + minor: arr[ 2 ] + 0, |
| 198 | + micro: arr[ 3 ] + 0, |
| 199 | + qualifier_appendix: "", |
| 200 | + qualifier_appendix_nbr: 100 |
| 201 | + }; |
| 202 | + |
| 203 | + // qualifier has an appendix? (BETA,SNAPSHOT) |
| 204 | + var qArr = listToArray( arr[ 4 ], '-' ); |
| 205 | + if ( qArr.len() == 1 && isNumeric( qArr[ 1 ] ) ) { |
| 206 | + sct.qualifier = qArr[ 1 ] + 0; |
| 207 | + } else if ( qArr.len() == 2 && isNumeric( qArr[ 1 ] ) ) { |
| 208 | + sct.qualifier = qArr[ 1 ] + 0; |
| 209 | + sct.qualifier_appendix = qArr[ 2 ]; |
| 210 | + if ( sct.qualifier_appendix == "SNAPSHOT" ) { |
| 211 | + sct.qualifier_appendix_nbr = 0; |
| 212 | + } else if ( sct.qualifier_appendix == "BETA" ) { |
| 213 | + sct.qualifier_appendix_nbr = 50; |
| 214 | + } else { |
| 215 | + sct.qualifier_appendix_nbr = 75; // every other appendix is better than SNAPSHOT |
| 216 | + } |
| 217 | + } else { |
| 218 | + sct.qualifier = qArr[ 1 ] + 0; |
| 219 | + sct.qualifier_appendix_nbr = 75; |
| 220 | + } |
| 221 | + |
| 222 | + return repeatString( "0", 2 - len( sct.major ) ) & sct.major |
| 223 | + & "." & repeatString( "0", 3 - len( sct.minor ) ) & sct.minor |
| 224 | + & "." & repeatString( "0", 3 - len( sct.micro ) ) & sct.micro |
| 225 | + & "." & repeatString( "0", 4 - len( sct.qualifier ) ) & sct.qualifier |
| 226 | + & "." & repeatString( "0", 3 - len( sct.qualifier_appendix_nbr ) ) & sct.qualifier_appendix_nbr; |
| 227 | + } |
| 228 | + |
| 229 | +} |
0 commit comments