Skip to content

[Tooling] Upload CDN builds as Internal first, publish as External later#2724

Merged
iangmaia merged 10 commits into
trunkfrom
ainfra-2102-cdn-builds-internal-visibility
Jun 12, 2026
Merged

[Tooling] Upload CDN builds as Internal first, publish as External later#2724
iangmaia merged 10 commits into
trunkfrom
ainfra-2102-cdn-builds-internal-visibility

Conversation

@iangmaia

@iangmaia iangmaia commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

Related issues

How AI was used in this PR

Claude Code implemented the Fastfile changes based on the approach designed in #2689 and refined through review of #2695, and assisted with rebasing and verification. All generated code was reviewed.

Proposed Changes

Today, production builds become publicly available on the Apps CDN as soon as finalize_release uploads them — before the Release Manager has a chance to verify them. This PR closes that gap by giving the team an internal testing window between finalize and publish:

  • finalize_release uploads Production builds with Internal visibility: downloadable by Automatticians for smoke-testing, but not served to users (the auto-update endpoint only serves External builds)
  • publish_release flips them to External (via the new update_apps_cdn_build_metadata toolkit action) right before publishing the GitHub release
  • Nightlies and Betas keep External visibility — they don't go through publish_release, so nothing would ever flip them
  • The CDN post IDs to flip are carried in the draft GitHub release body, so publish only touches builds from the latest finalization (an earlier draft-status approach, [Tooling] Upload CDN builds as drafts, publish in publish_release #2695, was abandoned because draft posts aren't easily testable by the RM)

Testing Instructions

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?
  • release-toolkit#701 merged, released (14.7.0), and bundled in this PR

🤖 Generated with Claude Code

Comment thread fastlane/Fastfile Outdated
release_name = "v#{version}"
cdn_post_ids = extract_cdn_post_ids_from_draft_release(release_name: release_name)

UI.user_error!("No CDN post IDs found in draft release #{release_name}. Cannot publish without updating CDN visibility.") if cdn_post_ids.empty?

@AliSoftware AliSoftware Mar 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if there is a (curl?) command we could run from developers' machines to ask the AppsCDN site for those post IDs if they are missing?

If we can find such a command, then maybe it would then be worth to include it in this UI.user_error! message as a suggestion for the developer to run it as a way to recover?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've tried a few things and added a message (hard coding the visibility code for internal) in 38e6abe.

Comment thread fastlane/Fastfile Outdated

UI.user_error!("No CDN post IDs found in draft release #{release_name}. Cannot publish without updating CDN visibility.") if cdn_post_ids.empty?

post_ids = cdn_post_ids.values.map(&:to_i)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Given the only usage we have for those post IDs is to get the list of the IDs (i.e. we don't care about the keys of that JSON in the first place), what's the point of storing the post IDs in the GitHub Release draft as a JSON object, as opposed to a simple comma-separated list of IDs?

<!-- CDN_POST_IDS:123,456,789 -->

This would:

  • Make this magic comment smaller and taking less visual space (i.e. less verbose if e.g. someone wanted to edit the draft to amend the release description and got this large JSON being a bit in the way, also avoiding risk of introduce a syntax error if accidentally making changes to that comment…)
  • Make it way easier to parse in extract_cdn_post_ids_from_draft_release (no need to parse the JSON, just have that helper return match[1].split(',').map(&:to_i) as an Array<Integer> instead of a Hash)
  • Make it easier to add that comment manually in the unlikely case that the Release Managers would hit this case and would want to recover from it manually by adding the list of post IDs themselves to unblock the process

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍 Good idea, it simplifies things a lot. Updated on 3739247.

Comment thread fastlane/Fastfile Outdated
Comment on lines +772 to +776
# Embed CDN post IDs so publish_release can update their visibility later
cdn_post_ids = builds.each_with_object({}) do |(key, build), hash|
hash[key] = build[:post_id] if build[:post_id]&.positive?
end
body += "\n<!-- CDN_POST_IDS:#{cdn_post_ids.to_json} -->" unless cdn_post_ids.empty?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Based on my previous suggestion comment about storing them as a simple comma-separate list instead:

Suggested change
# Embed CDN post IDs so publish_release can update their visibility later
cdn_post_ids = builds.each_with_object({}) do |(key, build), hash|
hash[key] = build[:post_id] if build[:post_id]&.positive?
end
body += "\n<!-- CDN_POST_IDS:#{cdn_post_ids.to_json} -->" unless cdn_post_ids.empty?
# Embed CDN post IDs so publish_release can update their visibility later
cdn_post_ids = builds.values.map(&:post_id).select(&:positive?)
body += "\n<!-- CDN_POST_IDS:#{cdn_post_ids.join(',')} -->" unless cdn_post_ids.empty?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated on 3739247.

Comment thread fastlane/Fastfile Outdated
Comment on lines +839 to +856
match = body.match(/<!-- CDN_POST_IDS:(\{.*?\}) -->/)
unless match
UI.important("Draft release #{release_name} found but no CDN post IDs embedded in the body.")
return {}
end

begin
parsed = JSON.parse(match[1])
rescue JSON::ParserError => e
UI.error("Failed to parse CDN post IDs from draft release #{release_name}: #{e.message}")
return {}
end

parsed.each do |key, value|
UI.user_error!("Invalid CDN post ID for '#{key}' in draft release #{release_name}: #{value.inspect} (expected positive integer)") unless value.is_a?(Integer) && value.positive?
end

parsed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Based on my previous suggestion comment about storing them as a simple comma-separate list instead:

Suggested change
match = body.match(/<!-- CDN_POST_IDS:(\{.*?\}) -->/)
unless match
UI.important("Draft release #{release_name} found but no CDN post IDs embedded in the body.")
return {}
end
begin
parsed = JSON.parse(match[1])
rescue JSON::ParserError => e
UI.error("Failed to parse CDN post IDs from draft release #{release_name}: #{e.message}")
return {}
end
parsed.each do |key, value|
UI.user_error!("Invalid CDN post ID for '#{key}' in draft release #{release_name}: #{value.inspect} (expected positive integer)") unless value.is_a?(Integer) && value.positive?
end
parsed
match = body.match(/<!-- CDN_POST_IDS:([0-9,]*) -->/)
unless match
UI.important("Draft release #{release_name} found but no CDN post IDs embedded in the body.")
return []
end
match[1]).split(',').map(&:to_i)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated on 3739247

@AliSoftware AliSoftware left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code changes in Fastlane looks good so approving to unblock

But 🎗️ reminder you'll need to first merge wordpress-mobile/release-toolkit#701 then releasing a new version then do a bundle update fastlane-plugin-wpmreleasetoolkit in this PR before it's actually ready to be merged.

@iangmaia

Copy link
Copy Markdown
Contributor Author

FYI: I haven't merged this PR yet as I need to double check the API updates and review the process with the team again. I the meantime I got busy with other tasks but will get back to this soon.

@iangmaia iangmaia force-pushed the ainfra-2102-cdn-builds-internal-visibility branch from 1133493 to bd713d9 Compare April 7, 2026 09:34

@mokagio mokagio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@iangmaia I'm guessing the recent update here means that you confirmed the flow with the team as mentioned in an earlier comment.

Approving to unblock so this can be merged once the conflicts on Fastfile have been addressed.

Comment thread fastlane/Fastfile
return {
media_url: media_url
media_url: media_url,
post_id: 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

While it doesn't take long to look at the rest of the code on top of this, seeing this 0 in the code threw me off.

What do you think of adding a little comment?

Suggested change
post_id: 0
# downstream checks for a positive post_id; 0 will result in no upload, as appropriate for a dry run
post_id: 0

iangmaia and others added 4 commits June 11, 2026 18:47
During finalize_release, all CDN builds are now uploaded with
visibility: internal. The CDN post IDs are embedded in the draft
GitHub release body. In publish_release, those IDs are read back
and update_apps_cdn_build_metadata flips visibility to external
before the GitHub release is published.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… through publish_release

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 11, 2026 16:57
@iangmaia iangmaia force-pushed the ainfra-2102-cdn-builds-internal-visibility branch from bd713d9 to 92fb626 Compare June 11, 2026 16:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adjusts the Studio release Fastlane flow so production CDN artifacts are uploaded with Internal visibility during finalize_release, then flipped to External visibility during publish_release right before the GitHub release is published—enabling an internal testing window between finalize and publish.

Changes:

  • Upload production CDN builds as Internal (nightly/beta remain External).
  • Persist Apps CDN post IDs in the draft GitHub release body for later retrieval.
  • On publish_release, read stored post IDs and batch-update CDN visibility to External before publishing the draft release.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread fastlane/Fastfile
Comment thread fastlane/Fastfile
iangmaia and others added 2 commits June 11, 2026 19:08
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
make_cdn_builds_public searched for a draft named 'v1.7.5' but
create_draft_github_release names drafts 'Version 1.7.5', so publish
would always fail to find the CDN post IDs. Also coerce post IDs to
Integer defensively before embedding them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@iangmaia iangmaia requested a review from a team as a code owner June 12, 2026 17:54
iangmaia and others added 3 commits June 12, 2026 20:43
…build_metadata

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ds-internal-visibility

# Conflicts:
#	Gemfile.lock
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 2 changed files in this pull request and generated 4 comments.

Comment thread fastlane/Fastfile
Comment thread fastlane/Fastfile
Comment thread fastlane/Fastfile
Comment thread fastlane/Fastfile
…sing CDN post IDs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 2 changed files in this pull request and generated no new comments.

@iangmaia iangmaia merged commit 1cb6055 into trunk Jun 12, 2026
12 checks passed
@iangmaia iangmaia deleted the ainfra-2102-cdn-builds-internal-visibility branch June 12, 2026 19:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants