Skip to content

feat: Allow editing description and keywords for iOS Releases#917

Open
Animesh-Ghosh wants to merge 14 commits intomainfrom
feat/ios-releases/allow-description-keywords-edit
Open

feat: Allow editing description and keywords for iOS Releases#917
Animesh-Ghosh wants to merge 14 commits intomainfrom
feat/ios-releases/allow-description-keywords-edit

Conversation

@Animesh-Ghosh
Copy link
Copy Markdown
Contributor

@Animesh-Ghosh Animesh-Ghosh commented Feb 22, 2026

Closes: https://github.com/orgs/tramlinehq/projects/5?pane=issue&itemId=94582410

NOTE: Getting tramlinehq/applelink#41 merged is a pre-condition for merging this PR.

Why

We want to allow updating the description and keywords metadata for App Store releases.

This addresses

Allowing the iOS release's description and keywords metadata to be updated from the UI.

Before After
image image

We also do a length validation for the release's keywords and description and show an helpful info icon for the keywords field, which links to a Apple developer page on "Choose accurate keywords that resonate".

We also store the draft versions of the invalid keywords and descriptions.

Scenario Screenshot
Length validation image
Keyword info icon Screenshot 2026-02-26 at 12 23 02 AM
Draft keywords and descriptions image
Description on App Store page Screenshot 2026-03-16 at 11 53 41 PM
App Store keyword search Screenshot 2026-03-17 at 12 26 20 AM

Locale-updates, before initiating production deployment

Locale Tramline Notes Tramline App Submission App Store Connect
en-US image image Screenshot 2026-03-05 at 2 25 23 AMScreenshot 2026-03-05 at 2 25 35 AM
hi image image Screenshot 2026-03-05 at 2 25 51 AMScreenshot 2026-03-05 at 2 26 01 AM
ja image image Screenshot 2026-03-05 at 2 26 17 AMScreenshot 2026-03-05 at 2 26 27 AM

Scenarios tested

  • Allows editing the iOS release's keywords as a comma-separated string from the UI
    • Persists the iOS release's keywords as a string array
    • Validates the length of the iOS release keywords
    • Persists invalid keywords as draft keywords and allows applying the draft changes
  • Allows editing the iOS release's description from the UI
    • Validates the length of the iOS release description
    • Persists invalid description as a draft description and allows applying the draft changes
  • Updates the iOS release's description and keywords metadata on App Store Connect
    • Updates the iOS release's description and keywords metadata for the non-default locale on App Store Connect

Summary by CodeRabbit

  • New Features

    • Replaced legacy textareas with a unified, interactive textarea component (live character counters, updated label styling); keywords show an info icon with guidance.
    • iOS description and keywords are now editable and included in App Store submission payloads.
  • Chores

    • DB migration adds draft fields for keywords and description.
    • Anonymization updated to handle new draft fields.
  • Tests

    • Added/updated tests for validations, limits, locale-aware metadata, and draft behavior.

@Animesh-Ghosh Animesh-Ghosh self-assigned this Feb 22, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Replaces partial-based size-limited textareas with a new Form::SizeLimitedTextareaComponent and updates live release metadata views to use it; adds draft_keywords (string array) and draft_description (text) with a migration; introduces iOS limits/constants (description ≤ 4000, keywords total ≤ 100), validations, and keywords_joined; controller now permits and parses description and keywords; App Store submission payload includes description and keywords; tests and anonymization rules updated.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant UI as LiveRelease::MetadataComponent
    participant Component as Form::SizeLimitedTextareaComponent
    participant Ctrl as ReleaseMetadataController
    participant Model as ReleaseMetadata
    participant DB as Database
    participant AppStore as AppStoreIntegration

    User->>UI: Edit description / keywords
    UI->>Component: Render textarea with live counter
    alt Submit published metadata
        UI->>Ctrl: POST update_all (description, keywords, ...)
        Ctrl->>Ctrl: Permit & parse params (keywords CSV -> Array)
        Ctrl->>Model: update_and_clear_drafts!(attrs)
        Model->>Model: Validate lengths & compute keywords_joined
        Model->>DB: Persist attributes, clear draft_* fields
        Ctrl->>AppStore: prepare_release(..., metadata_with_description_and_keywords)
    else Save draft
        UI->>Ctrl: Save draft request
        Ctrl->>Model: save_draft(draft_attrs)
        Model->>DB: Persist draft_description & draft_keywords
    end
Loading

Possibly related PRs

Poem

🐰 I nibble keys and shape each line,
Drafts tucked warm where character counts shine,
Four thousand letters, a hundred seeds sown,
I hop through metadata, making it known,
Tiny carrots of text, polished and grown 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Allow editing description and keywords for iOS Releases' clearly and specifically describes the main feature being added, matching the changeset which adds UI and backend support for editing these iOS release metadata fields.
Description check ✅ Passed The PR description provides comprehensive coverage with clear motivation, detailed implementation details with before/after screenshots, and extensive scenario testing.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/ios-releases/allow-description-keywords-edit
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Animesh-Ghosh Animesh-Ghosh force-pushed the feat/ios-releases/allow-description-keywords-edit branch from d17f764 to f13623f Compare February 22, 2026 20:19
@Animesh-Ghosh Animesh-Ghosh force-pushed the feat/ios-releases/allow-description-keywords-edit branch 2 times, most recently from 3627684 to 00d35da Compare March 4, 2026 13:56
@Animesh-Ghosh Animesh-Ghosh marked this pull request as ready for review March 4, 2026 21:04
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/controllers/release_metadata_controller.rb`:
- Around line 20-23: The iOS keywords parsing in release_metadata_controller
(inside the block that checks rm_params.key?(:ios)) calls
ios_params[:keywords].split unguarded and can raise NoMethodError; update the
ios_params assignment to handle nil by converting keywords to a string or
checking presence before split, split on "," then map(&:strip) and remove blank
entries (e.g. use to_s or presence and then
.split(",").map(&:strip).reject(&:blank?)) so missing or empty keywords do not
cause a 500.

In `@app/models/release_metadata.rb`:
- Around line 69-71: keywords_joined can call join on nil for unsaved
ReleaseMetadata instances; change its implementation to guard against nil by
using the same pattern as elsewhere (e.g., Array(attrs[:keywords'])) so replace
the direct keywords.join(",") with a nil-safe conversion like
Array(keywords).join(",") inside the ReleaseMetadata#keywords_joined method;
this aligns behavior with the keywords_length validator (triggered on ios?) and
prevents NoMethodError during validation.

In `@db/schema.rb`:
- Around line 723-724: Add the new user-content columns draft_keywords and
draft_description to the anonymization task by updating the anonymize list in
lib/tasks/anonymize.rake: locate the array or method that enumerates columns to
scrub (e.g., the list named anonymize_columns, USER_CONTENT_COLUMNS, or the
method anonymize_user_content) and append "draft_keywords" and
"draft_description" so they are included in the anonymization pass; ensure array
type handling for draft_keywords (array of strings) is preserved consistent with
existing array-column handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e8b86ee8-ade7-436d-8c2a-1d1623f1da8a

📥 Commits

Reviewing files that changed from the base of the PR and between ddd859f and 00d35da.

📒 Files selected for processing (10)
  • app/components/live_release/metadata_component.html.erb
  • app/components/live_release/metadata_component.rb
  • app/controllers/release_metadata_controller.rb
  • app/models/app_store_submission.rb
  • app/models/release_metadata.rb
  • app/views/shared/_size_limited_textarea.html.erb
  • db/migrate/20260225190552_add_draft_keywords_and_description_to_release_metadata.rb
  • db/schema.rb
  • spec/models/app_store_submission_spec.rb
  • spec/models/release_metadata_spec.rb

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/components/live_release/metadata_component.html.erb (1)

90-94: Consider using label_type: :base for Android release notes for visual consistency.

The iOS fields all use label_type: :base, but the Android release notes field uses the default (:default). This may cause visual inconsistency between platform sections.

♻️ Suggested change for consistency
 <%= render Form::SizeLimitedTextareaComponent.new(form: aF,
                                                   obj_method: :release_notes,
                                                   label_text: "Release Notes",
                                                   max_length: android_max_length,
-                                                  existing_value: android_metadata.release_notes) %>
+                                                  existing_value: android_metadata.release_notes,
+                                                  label_type: :base) %>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/live_release/metadata_component.html.erb` around lines 90 -
94, The Android release notes field uses the default label type causing visual
inconsistency; update the Form::SizeLimitedTextareaComponent invocation (the one
with form: aF, obj_method: :release_notes, label_text: "Release Notes",
max_length: android_max_length, existing_value: android_metadata.release_notes)
to include label_type: :base so it matches the iOS fields' styling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/components/live_release/metadata_component.html.erb`:
- Around line 90-94: The Android release notes field uses the default label type
causing visual inconsistency; update the Form::SizeLimitedTextareaComponent
invocation (the one with form: aF, obj_method: :release_notes, label_text:
"Release Notes", max_length: android_max_length, existing_value:
android_metadata.release_notes) to include label_type: :base so it matches the
iOS fields' styling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2ecdc33d-8f74-47df-ab18-4dce0638c35b

📥 Commits

Reviewing files that changed from the base of the PR and between 00d35da and 40634e4.

📒 Files selected for processing (4)
  • app/components/form/size_limited_textarea_component.html.erb
  • app/components/form/size_limited_textarea_component.rb
  • app/components/live_release/metadata_component.html.erb
  • app/views/shared/_size_limited_textarea.html.erb
💤 Files with no reviewable changes (1)
  • app/views/shared/_size_limited_textarea.html.erb

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/controllers/release_metadata_controller.rb`:
- Line 22: Replace the call to reject(&:blank?) in the assignment to
ios_params[:keywords] with compact_blank to satisfy RuboCop's Rails/CompactBlank
rule while preserving behavior; specifically update the expression that sets
ios_params[:keywords] (the line using ios_params[:keywords] =
ios_params[:keywords].split(",").map(&:strip).reject(&:blank?)) to use
compact_blank instead of reject(&:blank?).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 39052dc3-7500-44c5-999f-dfec23e36823

📥 Commits

Reviewing files that changed from the base of the PR and between 40634e4 and d948751.

📒 Files selected for processing (2)
  • app/controllers/release_metadata_controller.rb
  • lib/tasks/anonymize.rake

@Animesh-Ghosh Animesh-Ghosh force-pushed the feat/ios-releases/allow-description-keywords-edit branch 2 times, most recently from d07a264 to ec14bce Compare March 5, 2026 08:01
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/models/release_metadata.rb (1)

118-122: ⚠️ Potential issue | 🟡 Minor

Add nil guard to notes_length validation.

The release_notes column is nullable (schema shows no null: false and no default), and there's no presence validation to prevent nil values. If release_notes is nil, calling .length on line 119 will raise NoMethodError.

Suggested fix
 def notes_length
+  return if release_notes.blank?
   if release_notes.length > notes_max_length
     errors.add(:release_notes, :too_long, max_length: notes_max_length, platform: release_platform_run.platform)
   end
 end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/models/release_metadata.rb` around lines 118 - 122, The notes_length
validation calls release_notes.length and will crash if release_notes is nil;
update the notes_length method to guard against nil by either returning early
when release_notes is nil (e.g., return if release_notes.nil?) or by using a
safe string conversion (e.g., release_notes.to_s.length) before comparing to
notes_max_length, then keep the errors.add call unchanged (referencing
notes_length, release_notes, notes_max_length, and
errors.add(:release_notes,...)).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@app/models/release_metadata.rb`:
- Around line 118-122: The notes_length validation calls release_notes.length
and will crash if release_notes is nil; update the notes_length method to guard
against nil by either returning early when release_notes is nil (e.g., return if
release_notes.nil?) or by using a safe string conversion (e.g.,
release_notes.to_s.length) before comparing to notes_max_length, then keep the
errors.add call unchanged (referencing notes_length, release_notes,
notes_max_length, and errors.add(:release_notes,...)).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 02ea2442-85fd-419c-a13d-94dce7d12f55

📥 Commits

Reviewing files that changed from the base of the PR and between d948751 and ec14bce.

📒 Files selected for processing (13)
  • app/components/form/size_limited_textarea_component.html.erb
  • app/components/form/size_limited_textarea_component.rb
  • app/components/live_release/metadata_component.html.erb
  • app/components/live_release/metadata_component.rb
  • app/controllers/release_metadata_controller.rb
  • app/models/app_store_submission.rb
  • app/models/release_metadata.rb
  • app/views/shared/_size_limited_textarea.html.erb
  • db/migrate/20260225190552_add_draft_keywords_and_description_to_release_metadata.rb
  • db/schema.rb
  • lib/tasks/anonymize.rake
  • spec/models/app_store_submission_spec.rb
  • spec/models/release_metadata_spec.rb
💤 Files with no reviewable changes (1)
  • app/views/shared/_size_limited_textarea.html.erb
🚧 Files skipped from review as they are similar to previous changes (5)
  • spec/models/release_metadata_spec.rb
  • spec/models/app_store_submission_spec.rb
  • app/models/app_store_submission.rb
  • db/schema.rb
  • app/controllers/release_metadata_controller.rb

@Animesh-Ghosh Animesh-Ghosh force-pushed the feat/ios-releases/allow-description-keywords-edit branch 3 times, most recently from 81b590d to 5c65c8f Compare March 16, 2026 10:33
@Animesh-Ghosh Animesh-Ghosh force-pushed the feat/ios-releases/allow-description-keywords-edit branch from 5c65c8f to e166e79 Compare March 16, 2026 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant