Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Changelog

All notable changes to V2er iOS app will be documented in this file.

## v1.1.1 (Build 31)
1. Feature: Add feed filter menu with Reddit-style dropdown for better content filtering
2. Fix: Prevent crash when clicking Ignore/Report buttons without being logged in
3. Fix: Improve TestFlight beta distribution configuration
4. Feature: Enable automatic TestFlight beta distribution to public testers

## v1.1.0 (Build 30)
1. Feature: Initial public beta release
2. Fix: Resolve iOS build hanging at code signing step
3. Fix: Improve version management system using xcconfig
4. Feature: Centralized version configuration in Version.xcconfig

---

## How to Update Changelog

When updating the version in `V2er/Config/Version.xcconfig`:

1. Add a new version section at the top of this file
2. List all changes since the last version:
- Use "Feature:" for new features
- Use "Fix:" for bug fixes
- Use "Improvement:" for enhancements
- Use "Breaking:" for breaking changes

Example format:
```
## vX.Y.Z (Build N)
1. Feature: Description of new feature
2. Fix: Description of bug fix
3. Improvement: Description of enhancement
```

The changelog will be automatically extracted and used in TestFlight release notes.
54 changes: 53 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,63 @@ Tests are located in:

Currently contains only boilerplate test setup.

## Release Management

### Version and Changelog Workflow

Version information is centralized in `V2er/Config/Version.xcconfig`:
- `MARKETING_VERSION`: User-facing version (e.g., 1.1.1)
- `CURRENT_PROJECT_VERSION`: Build number (auto-incremented by CI)

**When updating the version for a new release:**

1. **Update Version.xcconfig**
```bash
# Edit V2er/Config/Version.xcconfig
MARKETING_VERSION = 1.2.0
```

2. **Update CHANGELOG.md**
Add a new section at the top with your changes:
```markdown
## v1.2.0 (Build XX)
1. Feature: Description of new feature
2. Fix: Description of bug fix
3. Improvement: Description of enhancement
```

3. **Commit and push to trigger release**
```bash
git add V2er/Config/Version.xcconfig CHANGELOG.md
git commit -m "chore: bump version to 1.2.0"
git push origin main
```

The CI pipeline will:
- Validate that CHANGELOG.md contains an entry for the new version
- Extract the changelog for TestFlight release notes
- Auto-increment build number
- Upload to TestFlight with your changelog

### Fastlane Commands

```bash
# Build and upload to TestFlight (requires changelog)
fastlane beta

# Distribute existing build to beta testers
fastlane distribute_beta

# Sync certificates and provisioning profiles
fastlane sync_certificates
```

## Important Notes

- Minimum iOS version: iOS 15.0
- Supported architectures: armv7, arm64
- Orientation: Portrait only on iPhone, all orientations on iPad
- UI Style: Light mode enforced
- Website submodule: Located at `website/` (separate repository)
- create PR should always use english
- Create PR should always use English
- **CHANGELOG.md is required** for all releases - the build will fail if the current version is missing from the changelog
14 changes: 13 additions & 1 deletion fastlane/Fastfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Fastfile for V2er iOS app

# Import changelog helper
require_relative 'changelog_helper'

default_platform(:ios)

platform :ios do
Expand Down Expand Up @@ -105,6 +108,11 @@ platform :ios do

desc "Build and upload to TestFlight"
lane :beta do
# Validate that changelog exists for current version
unless ChangelogHelper.validate_changelog_exists
UI.user_error!("Please update CHANGELOG.md with an entry for the current version before releasing!")
end

# Ensure we have the latest certificates
sync_certificates

Expand All @@ -116,6 +124,7 @@ platform :ios do
xcconfig_path = "../V2er/Config/Version.xcconfig"
xcconfig_content = File.read(xcconfig_path)
current_build_number = xcconfig_content.match(/CURRENT_PROJECT_VERSION = (\d+)/)[1].to_i
current_version = xcconfig_content.match(/MARKETING_VERSION = (.+)/)[1].strip

latest_testflight = latest_testflight_build_number(api_key: api_key)

Expand Down Expand Up @@ -154,6 +163,9 @@ platform :ios do
# Build the app
build_ipa

# Extract changelog for the current version
changelog_content = ChangelogHelper.extract_changelog(current_version)

# Upload to TestFlight
upload_to_testflight(
api_key: api_key,
Expand All @@ -164,7 +176,7 @@ platform :ios do
distribute_external: true, # Distribute to external testers (public beta)
distribute_only: false, # Upload and distribute in one action
groups: ["Public Beta", "External Testers", "Beta Testers"], # Public beta groups
changelog: "Bug fixes and improvements",
changelog: changelog_content, # Use changelog from CHANGELOG.md
notify_external_testers: true, # Send email notifications to external testers
uses_non_exempt_encryption: false, # Required for automatic distribution
submit_beta_review: true, # Automatically submit for Beta review
Expand Down
149 changes: 149 additions & 0 deletions fastlane/changelog_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# changelog_helper.rb
# Helper module to extract changelog entries from CHANGELOG.md

module ChangelogHelper
# Extract changelog for a specific version from CHANGELOG.md
# @param version [String] The version to extract (e.g., "1.1.1")
# @return [String] The changelog content for the specified version
def self.extract_changelog(version)
changelog_path = File.expand_path("../CHANGELOG.md", __dir__)

unless File.exist?(changelog_path)
UI.error("CHANGELOG.md not found at #{changelog_path}")
return "Bug fixes and improvements"
end

content = File.read(changelog_path)

# Match the version section, handling both "vX.Y.Z" and "X.Y.Z" formats
# Also capture optional build number like "v1.1.1 (Build 31)"
version_pattern = /^##\s+v?#{Regexp.escape(version)}(?:\s+\(Build\s+\d+\))?\s*$/

lines = content.lines
start_index = nil
end_index = nil

# Find the start of the version section
lines.each_with_index do |line, index|
if line.match?(version_pattern)
start_index = index
break
end
end

if start_index.nil?
UI.warning("Version #{version} not found in CHANGELOG.md")
UI.message("Available versions:")
lines.each do |line|
if line.match?(/^##\s+v?\d+\.\d+\.\d+/)
UI.message(" - #{line.strip}")
end
end
return "Bug fixes and improvements"
end

# Find the end of the version section (next ## heading or ---)
((start_index + 1)...lines.length).each do |index|
line = lines[index]
if line.match?(/^##\s+/) || line.match?(/^---/)
end_index = index
break
end
end

end_index ||= lines.length

# Extract the changelog content (skip the version header)
changelog_lines = lines[(start_index + 1)...end_index]

# Remove leading/trailing empty lines and convert to string
changelog = changelog_lines
.join("")
.strip

if changelog.empty?
UI.warning("No changelog content found for version #{version}")
return "Bug fixes and improvements"
end

# Format for TestFlight (convert numbered list to bullet points if needed)
# TestFlight supports basic formatting
formatted_changelog = format_for_testflight(changelog)

UI.success("Extracted changelog for version #{version}:")
UI.message(formatted_changelog)

formatted_changelog
end

# Format changelog content for TestFlight display
# @param content [String] Raw changelog content
# @return [String] Formatted changelog
def self.format_for_testflight(content)
# TestFlight supports:
# - Plain text
# - Line breaks
# - Basic formatting

# Convert numbered lists to bullet points for better readability
# "1. Feature: xxx" -> "β€’ Feature: xxx"
formatted = content.gsub(/^\d+\.\s+/, "β€’ ")

# Ensure we don't exceed TestFlight's changelog length limit (4000 chars)
if formatted.length > 3900
formatted = formatted[0...3900] + "\n\n(See full changelog at github.com/v2er-app/iOS)"
end

formatted
end

# Get the current version from Version.xcconfig
# @return [String] The current marketing version
def self.get_current_version
xcconfig_path = File.expand_path("../V2er/Config/Version.xcconfig", __dir__)

unless File.exist?(xcconfig_path)
UI.user_error!("Version.xcconfig not found at #{xcconfig_path}")
end

content = File.read(xcconfig_path)
version_match = content.match(/MARKETING_VERSION\s*=\s*(.+)/)

if version_match
version = version_match[1].strip
UI.message("Current version from Version.xcconfig: #{version}")
version
else
UI.user_error!("Could not find MARKETING_VERSION in Version.xcconfig")
end
end

# Validate that changelog exists for the current version
# @return [Boolean] True if changelog exists, false otherwise
def self.validate_changelog_exists
current_version = get_current_version
changelog_path = File.expand_path("../CHANGELOG.md", __dir__)

unless File.exist?(changelog_path)
UI.error("❌ CHANGELOG.md not found!")
UI.message("Please create CHANGELOG.md with an entry for version #{current_version}")
return false
end

content = File.read(changelog_path)
version_pattern = /^##\s+v?#{Regexp.escape(current_version)}/

if content.match?(version_pattern)
UI.success("βœ… Changelog entry found for version #{current_version}")
return true
else
UI.error("❌ No changelog entry found for version #{current_version}")
UI.message("Please add a changelog entry in CHANGELOG.md:")
UI.message("")
UI.message("## v#{current_version}")
UI.message("1. Feature/Fix: Description of changes")
UI.message("")
return false
end
end
end
Loading