Skip to content

feat(release): prefix app tags app/v so they leave SwiftPM's namespace - #79

Merged
prakashrj merged 1 commit into
mainfrom
feat/app-tag-prefix
Sep 3, 2026
Merged

feat(release): prefix app tags app/v so they leave SwiftPM's namespace#79
prakashrj merged 1 commit into
mainfrom
feat/app-tag-prefix

Conversation

@prakashrj

Copy link
Copy Markdown
Contributor

Delivers backlog 999.2 / audit W-1.

The coupling

This repo is both an app and a SwiftPM package, and SwiftPM claims every tag that parses as a version. So v0.1.1+9 was simultaneously the app's release marker and package version 0.1.1. An App Store metadata bump published a package version, and adopters got a bump from a commit that never mentioned SwiftPM.

The measurement contradicted the plan

The roadmap's third option was "publish the package from a separate tag series that SwiftPM resolves — needs checking whether SwiftPM can be pointed at a non-default tag scheme". I checked, by tagging this repo and resolving against it:

tags present .upToNextMajor(from: "0.1.0") resolved to
pkg-0.3.0, package/0.4.0, release/0.5.0, milestone/v0.2 0.1.1+9 — every prefixed tag ignored
v0.2 0.2.0 — a bare tag claimed immediately

That option does not exist. But the inverse works, and it was not in the options list at all: move the app tags out of SwiftPM's view and leave the package namespace clean.

app/v0.1.2+10   an app release; invisible to SwiftPM
0.2.0           a package release, cut deliberately

Neither remaining option was needed — the package did not have to move to its own repo, and the coupling did not have to be accepted. The two lifecycles now move independently in one repo, which the backlog note said was the thing that could not be had.

Implemented upstream

RELEASE_TAG_PREFIX in apple-shipkit#286, plus #287 fixing plumbing that #286 shipped incomplete — the value reached neither .bootstrap.env readers nor the fastlane subprocess, so ship.rb would have computed app/v0.1.2+10 and handed it to a process that still stripped v, leaving every artifact name built from the unstripped tag. Found while adopting it here, not by re-reading the diff.

Unset means v, so no other fork changes behaviour.

Verified end to end, on the real path

$ bundle exec ruby bin/compute-release-tag.rb
app/v0.1.1+10

$ RELEASE_TAG_PREFIX=v bundle exec ruby bin/compute-release-tag.rb
v0.1.1+10

.bootstrap.envConfigENVVersion.tag_prefix → tag. Env precedence and default-equivalence both proven, not inferred.

Compatibility

v0.1.0+6 and v0.1.1+9 stay published and remain the current package versions. Every reader strips the configured prefix and then falls back to a bare v, so pre-change tags still parse — ci/bump-asc-version.rb may still be handed either.

ci/check-release-commit.sh now says which kind of tag a ship is about to push. Its W-1 "this is also a package version" note fires only when the tag really is SwiftPM-visible:

  note This tag is an APP release only (prefix 'app/v').
       SwiftPM ignores tags that do not parse as a version, so this ship
       publishes NO package version — adopters stay where they are until
       somebody cuts a bare X.Y.Z tag on purpose.

Both branches exercised.

A package release now requires cutting a bare X.Y.Z tag on purpose. That is the point, and it is documented in AGENTS.md and .bootstrap.env.example.

Local: tag_prefix_test.rb 23/23, check-shell.sh clean, check-patch-registry.sh passes.

Delivers backlog 999.2 / audit W-1.

This repo is both an app and a SwiftPM package, and SwiftPM claims every
tag that parses as a version. So v0.1.1+9 was simultaneously the app's
release marker AND package version 0.1.1: an App Store metadata bump
published a package version, and adopters got a bump from a commit that
never mentioned SwiftPM.

Decided on a measurement, which contradicted the roadmap. Its third
option was "publish the package from a separate tag series SwiftPM
resolves". Tagging this repo and resolving against it:

  pkg-0.3.0, package/0.4.0, release/0.5.0, milestone/v0.2
      -> .upToNextMajor(from: "0.1.0") resolved 0.1.1+9 (all ignored)
  v0.2
      -> resolved 0.2.0 (claimed at once)

SwiftPM cannot be pointed at a non-default tag scheme, so that option
does not exist. The inverse works and was not in the list at all: move
the APP tags out of its view and leave the package namespace clean.
Neither remaining option was needed — the package did not have to move
repos, and the coupling did not have to be accepted.

Implemented upstream as RELEASE_TAG_PREFIX (apple-shipkit#286, plus #287
fixing plumbing #286 shipped incomplete: the value reached neither
.bootstrap.env readers nor the fastlane subprocess, so ship.rb would
have computed app/v0.1.2+10 and handed it to a process still stripping
"v"). Unset means "v", so no other fork changes.

Verified end to end here: bin/compute-release-tag.rb returns
app/v0.1.1+10 from .bootstrap.env, and RELEASE_TAG_PREFIX=v returns
v0.1.1+10 — env precedence and default-equivalence both proven on the
real path.

v0.1.0+6 and v0.1.1+9 stay published and remain the current package
versions; every reader strips the configured prefix then falls back to a
bare "v", so pre-change tags still parse.
ci/check-release-commit.sh now says which kind of tag a ship is about to
push — its W-1 note fires only when the tag really is SwiftPM-visible.
A package release now requires cutting a bare X.Y.Z tag on purpose.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@prakashrj
prakashrj merged commit 4e8e988 into main Sep 3, 2026
21 checks passed
@prakashrj
prakashrj deleted the feat/app-tag-prefix branch September 3, 2026 08:23
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