Skip to content

AINFRA-2488 Add TV parity to Android CI and releases - #5682

Draft
iangmaia wants to merge 2 commits into
mainfrom
iangmaia/ainfra-2488-tv-ci-release-parity
Draft

AINFRA-2488 Add TV parity to Android CI and releases#5682
iangmaia wants to merge 2 commits into
mainfrom
iangmaia/ainfra-2488-tv-ci-release-parity

Conversation

@iangmaia

@iangmaia iangmaia commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Fixes AINFRA-2488

Description

Adds Android TV in CI and release automation so TV artifacts are validated, distributed and rolled out with the main app, Automotive, and Wear.

Testing Instructions

  1. Run bundle exec rubocop fastlane/Fastfile and ./gradlew spotlessCheck.
  2. Run ./gradlew :tv:testDebugUnitTest.
  3. Run ./gradlew :app:lintRelease :automotive:lintRelease :tv:lintRelease :wear:lintRelease.
  4. Run ./gradlew :tv:assembleRelease -PIS_TV_BUILD=true -PskipSentryProguardMappingUpload=true.
  5. Run bundle exec fastlane build_bundle app:tv and confirm the TV AAB uses the expected version suffix and code offset.
  6. In Buildkite, confirm the TV release APK artifact, TV lint/SARIF output, and optional 📺 TV prototype link.

Screenshots or Screencast

Not applicable — this PR does not change UI.

Checklist

  • If this is a user-facing change, I have added an entry in CHANGELOG.md
  • Ensure the linter passes (./gradlew spotlessApply to automatically apply formatting/linting)
  • I have considered whether it makes sense to add tests for my changes
  • All strings that need to be localized are in modules/services/localization/src/main/res/values/strings.xml
  • Any jetpack compose components I added or changed are covered by compose previews
  • I have updated (or requested that someone edit) the Event Horizon schema to reflect any new or changed analytics.

@iangmaia iangmaia self-assigned this Jul 29, 2026
@iangmaia iangmaia added [Type] Tooling Related to the Gradle build scripts and the setup or maintenance of the project build process. [Area] TV labels Jul 29, 2026
@iangmaia iangmaia added this to the 8.18 milestone Jul 29, 2026
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Claude finished @iangmaia's task in 4m 30s —— View job


Code review

  • Read the PR diff
  • Verify tv module + IS_TV_BUILD / version-code offset wiring
  • Review Fastfile changes (tracks, artifact paths, prototype upload)
  • Review Buildkite pipeline + lint script changes
  • Post review (4 inline comments)

The change is well-scoped and internally consistent — TV slots into the existing three-form-factor abstractions rather than bolting on a parallel path, and the plumbing this depends on is already in place. I verified the pieces that could silently produce wrong artifacts:

Check Result
IS_TV_BUILD consumed by Gradle dependencies.gradle.kts:31
Fastfile offset +150_000 vs Gradle versionCodeDifferenceBetweenAppAndTv ✅ both 150000 (dependencies.gradle.kts:5); current versionCode=9443, so no overlap risk
Version name suffix t dependencies.gradle.kts:46
get_app_key/get_app_display_name resolve the TV APK ✅ project dir is tvtv-debugProd.apk:tv📺 TV
/#{platform}/ path filter for the S3 loop ✅ no collision with app/wear/automotive
tv:assemble for debugProd exists tv/build.gradle.kts has debugProd; only prototype is disabled, and the lane uses DebugProd for non-mobile
tv/lint-baseline.xml, tv/google-services.json ✅ baseline committed; .configure already copies a TV google-services.json
Unit tests ✅ CI runs bare testDebugUnitTest, so :tv was already covered

I could not run the verification commands from the testing instructions — bash -n, ruby -c, rubocop, and gradlew are all blocked by the sandbox in this environment, and WebSearch wasn't granted. Everything below is from reading the code.

Findings

1. APPS ordering puts TV ahead of wear — a TV-only failure would block wear (inline) · fastlane/Fastfile:73

build_and_upload_to_play_store loops APPS with no per-app rescue. A missing tv:beta track, a Play TV listing-validation error, or a universal APK Play hasn't generated yet all raise — and at index 2 that aborts the lane before wear is built or uploaded, and before create_gh_release. Moving TV to the end of the array confines the blast radius of the new, unproven form factor to itself. This is the one item I'd treat as more than a nit.

2. tv:beta / tv:production need confirming against Play Console (inline) · fastlane/Fastfile:22-23

wear:*/automotive:* are known-good, but I have no way to verify the TV prefix from here, and form-factor tracks must already exist in Play Console — the API won't create them on upload. Combined with finding 1, a wrong value fails mid-release with app and automotive already uploaded as drafts. Worth a one-command check before the next release build.

3. lint.sh should pass --continue (inline) · .buildkite/commands/lint.sh:20

abortOnError = true (build.gradle.kts:251) means the first failing module in the combined invocation stops the rest, so their SARIF files are never produced and the matching upload_sarif_to_github calls upload nothing. Pre-existing, but TV is a new module with a near-empty baseline and now sits ahead of wear. --continue still exits non-zero, so the exit-code logic is unaffected.

4. Parity gap not covered: merged-manifest diff · .buildkite/commands/diff-merged-manifest.sh:19-27

That script runs comment_with_manifest_diff for app, wear, and automotive but not tv, so TV manifest regressions won't surface in PRs. Since the PR's goal is TV parity across CI, adding a fourth call there seems in scope. Fix this →

5. Minor / informational

  • assemble-release-apk.sh doesn't pass -PIS_TV_BUILD=true, so the new CI TV release APK carries the mobile versionCode and versionName (no t suffix, no +150000). Automotive and wear have the identical gap, so this PR is consistent with the status quo — but note the artifact isn't the version identity that ships, and it shares package + versionCode with the mobile APK. Your testing instruction 6 does pass the flag; fixing this for all four modules would be a reasonable separate change.
  • The 50k/100k/150k offsets are now duplicated in four places across Ruby and Gradle. A drift here fails silently (wrong version code uploaded or rolled out). The doc comment pointing at dependencies.gradle.kts helps; nothing to change now.
  • update_rollouts will log an extra "not found" UI.important for TV on every rollout until TV actually ships. Harmless, and the not_found_variants.count == APPS.count guard still behaves correctly.
  • Group-label nit on .buildkite/pipeline.yml:70 (inline).

No security concerns: no new secrets, credentials, or endpoints; the TV google-services.json reuses the existing encrypted .configure entry. Performance impact is one additional assemble job (parallel, so no critical-path change) plus one more lint module and Play upload in the release lane.
Branch

@dangermattic

Copy link
Copy Markdown
Collaborator
1 Message
📖 This PR is still a Draft: some checks will be skipped.

Generated by 🚫 Danger

@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Comment thread fastlane/Fastfile
APPS_TV = 'tv'
APPS_WEAR = 'wear'
APPS = [APPS_APP, APPS_AUTOMOTIVE, APPS_WEAR].freeze
APPS = [APPS_APP, APPS_AUTOMOTIVE, APPS_TV, APPS_WEAR].freeze

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Consider putting TV last in APPS rather than third.

build_and_upload_to_play_store iterates APPS with no per-app rescue (unlike update_rollouts, which tolerates missing variants). Every step inside the loop can hard-fail for a brand-new form factor:

  • upload_to_play_store(track: 'tv:beta') → fails if the TV track doesn't exist yet in Play Console
  • download_universal_apk_from_google_play(version_code: <build_code + 150_000>) → fails if Play hasn't produced a universal APK for that TV version code
  • Play's TV-specific listing/asset validation

With TV inserted at index 2, any of those aborts the lane before wear is built or uploaded, and create_gh_release never runs — so a TV-only problem regresses two already-shipping form factors and the whole GitHub release. Appending TV keeps the failure at the tail:

Suggested change
APPS = [APPS_APP, APPS_AUTOMOTIVE, APPS_TV, APPS_WEAR].freeze
APPS = [APPS_APP, APPS_AUTOMOTIVE, APPS_WEAR, APPS_TV].freeze

(Order is irrelevant for update_rollouts, so nothing else changes.)

Comment thread fastlane/Fastfile
Comment on lines +22 to +23
PLAY_STORE_TRACK_TV_BETA = 'tv:beta'
PLAY_STORE_TRACK_TV_PRODUCTION = 'tv:production'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Please confirm these track identifiers against the actual Play Console before the next release build.

wear:* and automotive:* are known-good form-factor track names, but I couldn't verify tv:beta / tv:production from here (no network access in this environment). A wrong identifier here is not a soft failure: upload_to_play_store raises, and per the ordering issue above it would abort mid-release with app/automotive already uploaded as drafts.

Two things worth checking:

  1. The exact form-factor prefix Google uses for Android TV in the Publishing API.
  2. That the track actually exists in Play Console for au.com.shiftyjelly.pocketcasts — form-factor tracks have to be created there first; the API won't create them on upload.

A quick way to confirm both:

bundle exec fastlane run google_play_track_version_codes \
  package_name:au.com.shiftyjelly.pocketcasts track:tv:beta \
  json_key:google-upload-credentials.json

Comment on lines +20 to +21
./gradlew :automotive:lintRelease :tv:lintRelease :wear:lintRelease
other_apps_lint_exit_code=$?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggest --continue here now that three modules share one invocation.

abortOnError = true is set globally (build.gradle.kts:251), so the first module whose lint fails stops the invocation and the remaining lintRelease tasks never run. Their SARIF files are then never generated, and the corresponding upload_sarif_to_github calls at lines 31–32 silently upload nothing — so a TV lint error hides the wear report, and an automotive error hides both.

That's exactly the failure mode the comment on line 16 works around for :app:. It existed with two modules, but TV is a new module with a near-empty baseline (tv/lint-baseline.xml is 191 bytes) and it now sits before wear in the task list, so it's more likely to bite.

Suggested change
./gradlew :automotive:lintRelease :tv:lintRelease :wear:lintRelease
other_apps_lint_exit_code=$?
./gradlew --continue :automotive:lintRelease :tv:lintRelease :wear:lintRelease
other_apps_lint_exit_code=$?

--continue still yields a non-zero exit code, so lint_exit_code handling below is unaffected — you just get all four reports uploaded on a failing run.

Fix this →

Comment thread .buildkite/pipeline.yml
- "**/build/instrumented-tests/**/*"

- group: "Assemble release APKs"
- group: "Assemble Mobile, Automotive, TV, and Wear release APKs"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit: enumerating form factors in the group label means editing it for every new one (and it now says "Mobile" while the step below says "app"). Something like "Assemble release APKs (all form factors)" stays accurate on its own. Non-blocking either way.

@wpmobilebot wpmobilebot modified the milestones: 8.18, 8.19 Aug 3, 2026
@wpmobilebot

Copy link
Copy Markdown
Collaborator

Version 8.18 has now entered code-freeze, so the milestone of this PR has been updated to 8.19.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Area] TV [Type] Tooling Related to the Gradle build scripts and the setup or maintenance of the project build process.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants