Skip to content

Give every promotion build of a chain the same version - #4

Merged
donat merged 1 commit into
teamcity-configfrom
donat/consistent-promotion-version
Sep 11, 2026
Merged

donat merged 1 commit into
teamcity-configfrom
donat/consistent-promotion-version

Conversation

@donat

@donat donat commented Sep 8, 2026

Copy link
Copy Markdown
Member

Companion to eclipse-buildship#1372.

The promotion builds of one release pipeline each computed their own version qualifier from the moment they happened to run. A chain walks through 33 Eclipse version streams one after the other and takes hours, so the same release was published as 3.1.12.v20260908-0412 in one stream and as 3.1.12.v20260908-0631 in the next, under a different update site folder name each time.

A new Determine version build now runs first in every promotion chain and determines the build instant once, as seconds since the epoch, published as its build number. All 132 individual promotion builds take a snapshot dependency on it and pass the instant on with -PbuildTimestamp, and Gradle reconstructs the qualifier from it. The build contributes the raw instant and nothing else, so the v prefix, the date layout, the minute granularity and the -s/-m suffixes all stay in build.gradle where they already were.

The build checks out nothing (CheckoutMode.MANUAL). The VCS root is attached only so that TeamCity scopes dependency reuse to a revision. Re-running a single failed promotion then picks up the instant its chain started with instead of minting a new one, which is the same defect resurfacing on the retry path. Without a VCS root any old successful build would count as suitable.

The four aggregate trigger builds and Tag revision and increment version number are unchanged: tag and incrementVersion read version.txt and never touch the qualifier.

Verification

mvn -f .teamcity/pom.xml teamcity-configs:generate succeeds (needs Java 21, the 2026.2 plugin is compiled for it). Auditing the 200 generated build types:

with -PbuildTimestamp param : 132
with snapshot dependency    : 132
mismatches                  : none
dep but no param            : none

  Promotion_Milestone                    33
  Promotion_Release                      33
  Promotion_Snapshot                     33
  Promotion_Snapshot_from_Sanity_check   33

The five promotion build types without the parameter are the four aggregate triggers and Determine version itself, none of which run uploadUpdateSite.

In every one of the 132 the parameter reference and the dependency name the same build type:

-PbuildTimestamp=%dep.RootProjectId_Promotion_Determine_version.build.number%
<depend-on sourceBuildTypeId="RootProjectId_Promotion_Determine_version">

RootProjectId is the placeholder the local generator substitutes for the project the settings belong to; on the server both sides resolve to Tooling_Buildship_.... They are composed from the same id.value, so they cannot drift apart. Worth knowing for anyone writing a %dep.% reference here: id!!.value already carries that prefix, and prepending DslContext.projectId yourself yields RootProjectId_RootProjectId_....

The generated script step is intact after Kotlin and TeamCity escaping, with %%s surviving as a literal %s:

TIMESTAMP=$(date +%%s)
echo "Promoting with build instant $TIMESTAMP ($(date -u -d @$TIMESTAMP))"
echo "##teamcity[buildNumber '$TIMESTAMP']"

What local generation cannot prove is that TeamCity resolves the %dep.% reference at run time. The first promotion chain to run confirms it: the Gradle step's command line shows the epoch instead of the parameter reference. If it were wrong the build fails at once, since the companion change rejects an empty or non-numeric -PbuildTimestamp rather than falling back to the current time.

The promotion builds of one release pipeline each computed their own
version qualifier from the moment they happened to run. A chain walks
through 33 Eclipse version streams one after the other and takes hours,
so the same release was published as 3.1.12.v20260908-0412 in one stream
and as 3.1.12.v20260908-0631 in the next.

A new "Determine version" build now runs first in every promotion chain
and determines the build instant once, as seconds since the epoch,
published as its build number. All 132 individual promotion builds take
a snapshot dependency on it and pass the instant to Gradle, which
reconstructs the qualifier from it. The build itself knows nothing about
how Buildship composes a version number.

It checks out nothing. The VCS root is attached only so that TeamCity
scopes dependency reuse to a revision: re-running a single failed
promotion then picks up the instant its chain started with, rather than
minting a new one and publishing that Eclipse version under a folder
name of its own.
@donat
donat force-pushed the donat/consistent-promotion-version branch from 3cf6837 to 6a44fce Compare September 11, 2026 07:28
@donat
donat marked this pull request as ready for review September 11, 2026 07:31
@donat
donat merged commit 462904d into teamcity-config Sep 11, 2026
1 check failed
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