| 
 | 1 | +# changelog_helper.rb  | 
 | 2 | +# Helper module to extract changelog entries from CHANGELOG.md  | 
 | 3 | + | 
 | 4 | +module ChangelogHelper  | 
 | 5 | +  # Extract changelog for a specific version from CHANGELOG.md  | 
 | 6 | +  # @param version [String] The version to extract (e.g., "1.1.1")  | 
 | 7 | +  # @return [String] The changelog content for the specified version  | 
 | 8 | +  def self.extract_changelog(version)  | 
 | 9 | +    changelog_path = File.expand_path("../CHANGELOG.md", __dir__)  | 
 | 10 | + | 
 | 11 | +    unless File.exist?(changelog_path)  | 
 | 12 | +      UI.error("CHANGELOG.md not found at #{changelog_path}")  | 
 | 13 | +      return "Bug fixes and improvements"  | 
 | 14 | +    end  | 
 | 15 | + | 
 | 16 | +    content = File.read(changelog_path)  | 
 | 17 | + | 
 | 18 | +    # Match the version section, handling both "vX.Y.Z" and "X.Y.Z" formats  | 
 | 19 | +    # Also capture optional build number like "v1.1.1 (Build 31)"  | 
 | 20 | +    version_pattern = /^##\s+v?#{Regexp.escape(version)}(?:\s+\(Build\s+\d+\))?\s*$/  | 
 | 21 | + | 
 | 22 | +    lines = content.lines  | 
 | 23 | +    start_index = nil  | 
 | 24 | +    end_index = nil  | 
 | 25 | + | 
 | 26 | +    # Find the start of the version section  | 
 | 27 | +    lines.each_with_index do |line, index|  | 
 | 28 | +      if line.match?(version_pattern)  | 
 | 29 | +        start_index = index  | 
 | 30 | +        break  | 
 | 31 | +      end  | 
 | 32 | +    end  | 
 | 33 | + | 
 | 34 | +    if start_index.nil?  | 
 | 35 | +      UI.warning("Version #{version} not found in CHANGELOG.md")  | 
 | 36 | +      UI.message("Available versions:")  | 
 | 37 | +      lines.each do |line|  | 
 | 38 | +        if line.match?(/^##\s+v?\d+\.\d+\.\d+/)  | 
 | 39 | +          UI.message("  - #{line.strip}")  | 
 | 40 | +        end  | 
 | 41 | +      end  | 
 | 42 | +      return "Bug fixes and improvements"  | 
 | 43 | +    end  | 
 | 44 | + | 
 | 45 | +    # Find the end of the version section (next ## heading or ---)  | 
 | 46 | +    ((start_index + 1)...lines.length).each do |index|  | 
 | 47 | +      line = lines[index]  | 
 | 48 | +      if line.match?(/^##\s+/) || line.match?(/^---/)  | 
 | 49 | +        end_index = index  | 
 | 50 | +        break  | 
 | 51 | +      end  | 
 | 52 | +    end  | 
 | 53 | + | 
 | 54 | +    end_index ||= lines.length  | 
 | 55 | + | 
 | 56 | +    # Extract the changelog content (skip the version header)  | 
 | 57 | +    changelog_lines = lines[(start_index + 1)...end_index]  | 
 | 58 | + | 
 | 59 | +    # Remove leading/trailing empty lines and convert to string  | 
 | 60 | +    changelog = changelog_lines  | 
 | 61 | +      .join("")  | 
 | 62 | +      .strip  | 
 | 63 | + | 
 | 64 | +    if changelog.empty?  | 
 | 65 | +      UI.warning("No changelog content found for version #{version}")  | 
 | 66 | +      return "Bug fixes and improvements"  | 
 | 67 | +    end  | 
 | 68 | + | 
 | 69 | +    # Format for TestFlight (convert numbered list to bullet points if needed)  | 
 | 70 | +    # TestFlight supports basic formatting  | 
 | 71 | +    formatted_changelog = format_for_testflight(changelog)  | 
 | 72 | + | 
 | 73 | +    UI.success("Extracted changelog for version #{version}:")  | 
 | 74 | +    UI.message(formatted_changelog)  | 
 | 75 | + | 
 | 76 | +    formatted_changelog  | 
 | 77 | +  end  | 
 | 78 | + | 
 | 79 | +  # Format changelog content for TestFlight display  | 
 | 80 | +  # @param content [String] Raw changelog content  | 
 | 81 | +  # @return [String] Formatted changelog  | 
 | 82 | +  def self.format_for_testflight(content)  | 
 | 83 | +    # TestFlight supports:  | 
 | 84 | +    # - Plain text  | 
 | 85 | +    # - Line breaks  | 
 | 86 | +    # - Basic formatting  | 
 | 87 | + | 
 | 88 | +    # Convert numbered lists to bullet points for better readability  | 
 | 89 | +    # "1. Feature: xxx" -> "• Feature: xxx"  | 
 | 90 | +    formatted = content.gsub(/^\d+\.\s+/, "• ")  | 
 | 91 | + | 
 | 92 | +    # Ensure we don't exceed TestFlight's changelog length limit (4000 chars)  | 
 | 93 | +    if formatted.length > 3900  | 
 | 94 | +      formatted = formatted[0...3900] + "\n\n(See full changelog at github.com/v2er-app/iOS)"  | 
 | 95 | +    end  | 
 | 96 | + | 
 | 97 | +    formatted  | 
 | 98 | +  end  | 
 | 99 | + | 
 | 100 | +  # Get the current version from Version.xcconfig  | 
 | 101 | +  # @return [String] The current marketing version  | 
 | 102 | +  def self.get_current_version  | 
 | 103 | +    xcconfig_path = File.expand_path("../V2er/Config/Version.xcconfig", __dir__)  | 
 | 104 | + | 
 | 105 | +    unless File.exist?(xcconfig_path)  | 
 | 106 | +      UI.user_error!("Version.xcconfig not found at #{xcconfig_path}")  | 
 | 107 | +    end  | 
 | 108 | + | 
 | 109 | +    content = File.read(xcconfig_path)  | 
 | 110 | +    version_match = content.match(/MARKETING_VERSION\s*=\s*(.+)/)  | 
 | 111 | + | 
 | 112 | +    if version_match  | 
 | 113 | +      version = version_match[1].strip  | 
 | 114 | +      UI.message("Current version from Version.xcconfig: #{version}")  | 
 | 115 | +      version  | 
 | 116 | +    else  | 
 | 117 | +      UI.user_error!("Could not find MARKETING_VERSION in Version.xcconfig")  | 
 | 118 | +    end  | 
 | 119 | +  end  | 
 | 120 | + | 
 | 121 | +  # Validate that changelog exists for the current version  | 
 | 122 | +  # @return [Boolean] True if changelog exists, false otherwise  | 
 | 123 | +  def self.validate_changelog_exists  | 
 | 124 | +    current_version = get_current_version  | 
 | 125 | +    changelog_path = File.expand_path("../CHANGELOG.md", __dir__)  | 
 | 126 | + | 
 | 127 | +    unless File.exist?(changelog_path)  | 
 | 128 | +      UI.error("❌ CHANGELOG.md not found!")  | 
 | 129 | +      UI.message("Please create CHANGELOG.md with an entry for version #{current_version}")  | 
 | 130 | +      return false  | 
 | 131 | +    end  | 
 | 132 | + | 
 | 133 | +    content = File.read(changelog_path)  | 
 | 134 | +    version_pattern = /^##\s+v?#{Regexp.escape(current_version)}/  | 
 | 135 | + | 
 | 136 | +    if content.match?(version_pattern)  | 
 | 137 | +      UI.success("✅ Changelog entry found for version #{current_version}")  | 
 | 138 | +      return true  | 
 | 139 | +    else  | 
 | 140 | +      UI.error("❌ No changelog entry found for version #{current_version}")  | 
 | 141 | +      UI.message("Please add a changelog entry in CHANGELOG.md:")  | 
 | 142 | +      UI.message("")  | 
 | 143 | +      UI.message("## v#{current_version}")  | 
 | 144 | +      UI.message("1. Feature/Fix: Description of changes")  | 
 | 145 | +      UI.message("")  | 
 | 146 | +      return false  | 
 | 147 | +    end  | 
 | 148 | +  end  | 
 | 149 | +end  | 
0 commit comments