Implement new rules for release versions (#133)

## Summary

We need to implement a simple versioning schema for hermes-windows releases from release branches and
pre-releases from the main branch.
Thanks a lot to @dannyvv who implemented the version generation for this PR.

## The versioning schema

The versioning schema is described in the new `doc\WindowsVersioning.md ` document:
- The **pre-release package versions** built in the `main` branch look like
**0.0.0-2209.9002-8af7870c** where the `<major>.<minor>.<patch>` versions are
always `'0.0.0'`, and the prerelease part that follows after `'-'` is
`'yyMM.drrr-hhhhhhhh'`. Where `'yy'` are two numbers for the year, `'MM'` are
two numbers for the month, `'d'` is the day without `0` prefix, `'rrr'` is a
three digit number for the today's revision, and `'hhhhhhhh'` are the first 8
hexadecimal numbers from the source GitHub commit hash.
- The **pre-release file versions** look like `0.0.2209.9002` where the the
encoding is `'0.0.yyMM.drrr'`. Where numbers after `'0.0.'` have the same
encoding as for the pre-release package version.
- The **released package versions** use the usual semantic schema which is
based on RNW release numbers like `0.70.1`, where the `'0.70'` is the
`<major>.<minor>` release of RNW and RN, and the last number is a `'patch'`
number for that release. Note that the `'patch'` number will not match the
`'patch'` number of the RNW. We are going to generate the `'patch'` number
using ADO build revision with format `'r'` that avoids `0` prefixes for the
version to be valid semantic version.
- The `release file versions` look like `0.70.1.0` to match the version of the package. The last part of the file version number is always `0`.

Note, that the release version is going to be generated by the release pipeline.
We do not expect the release version to be set somewhere in the source files or by using the processes like beachball.

## Implementation details

- The `publish.yml` has a new `Setup` job that generates version numbers for the package and files using the new `setVersionNumber.js` script. The result of this job is used by `jobs.yml` to create two new variables `semanticVersion` and `fileVersion`. One is used for package semantic version and the other for the DLL/EXE file versions.
- The `semanticVersion` and `fileVersion` are passed to `cibuild.ps1` where they are used to update CMake project version and `package.json` version. The `fileVersion` is passed to CMake build as `HERMES_FILE_VERSION` definition.
- We ensure that `hermes.dll`, `inspector.dll`, and `hermes.exe` projects have the `version.rc` generated with the product and file versions, and with other meta-data settings.
- The generated Nuget packages pickup their versions from the `package.json` file.
- We set back versions in `package.json` and `CMakeLists.txt` to `0.12.0` to match `facebook/hermes` repo. This is done to reduce the diff between the two repos. We are always going to generate these versions in our release pipeline.
- The `ReactNative.Hermes.Windows.Fat.nuspec` is updated to match the `ReactNative.Hermes.Windows.nuspec` because it was missing some files such as `License`, and also had some unnecessary files such as `ninja` build scripts. Now the only extra files there are the symbol `*.pdb` files.
This commit is contained in:
Vladimir Morozov 2022-09-29 20:54:52 -07:00 коммит произвёл GitHub
Родитель 016aab4e0c
Коммит 16062c0cd1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 321 добавлений и 26 удалений

Просмотреть файл

@ -12,9 +12,19 @@
<repository type="git" url="$RepoUri$" commit="$CommitId$" />
</metadata>
<files>
<file src="$nugetroot$\lib\**\*.*" target="lib"/>
<file src="$nugetroot$\build\**\*.*" target="build"/>
<file src="$nugetroot$\license\*.*" target="license"/>
<file src="$nugetroot$\lib\native\debug\arm64\**\*.*" target="lib/native/debug/arm64" exclude="**\*.manifest;**\*.iobj;**\*.ipdb;**\*.exp;**\*.ilk" />
<file src="$nugetroot$\lib\native\release\arm64\**\*.*" target="lib/native/release/arm64" exclude="**\*.manifest;**\*.iobj;**\*.ipdb;**\*.exp;**\*.ilk" />
<file src="$nugetroot$\lib\native\debug\x64\**\*.*" target="lib/native/debug/x64" exclude="**\*.manifest;**\*.iobj;**\*.ipdb;**\*.exp;**\*.ilk" />
<file src="$nugetroot$\lib\native\release\x64\**\*.*" target="lib/native/release/x64" exclude="**\*.manifest;**\*.iobj;**\*.ipdb;**\*.exp;**\*.ilk" />
<file src="$nugetroot$\lib\native\debug\x86\**\*.*" target="lib/native/debug/x86" exclude="**\*.manifest;**\*.iobj;**\*.ipdb;**\*.exp;**\*.ilk" />
<file src="$nugetroot$\lib\native\release\x86\**\*.*" target="lib/native/release/x86" exclude="**\*.manifest;**\*.iobj;**\*.ipdb;**\*.exp;**\*.ilk" />
<file src="$nugetroot$\lib\uap\_._" target="lib/uap/_._" />
<file src="$nugetroot$\build\native\include\**\*.*" target="build/native/include" />
<file src="$nugetroot$\build\native\ReactNative.Hermes.Windows.targets" target="build/native/ReactNative.Hermes.Windows.targets" />
<file src="$nugetroot$\build\uap\ReactNative.Hermes.Windows.targets" target="build/uap/ReactNative.Hermes.Windows.targets" />
<file src="$nugetroot$\license\*" target="license"/>
<file src="$nugetroot$\tools\**\*.*" target="tools"/>
</files>
</package>

Просмотреть файл

@ -1,7 +1,15 @@
parameters:
- name: dependsOnSetup
type: boolean
default : false
jobs:
- job: Build
timeoutInMinutes: 120
displayName: Build Hermes
dependsOn:
- ${{ if parameters.dependsOnSetup }}:
- Setup
strategy:
matrix:
DebugX64:
@ -22,7 +30,9 @@ jobs:
ReleaseARM64:
BuildConfiguration: release
BuildPlatform: arm64
variables:
semanticVersion: $[ dependencies.Setup.outputs['setVersions.semanticVersion'] ]
fileVersion: $[ dependencies.Setup.outputs['setVersions.fileVersion'] ]
steps:
- task: PowerShell@2
displayName: Run the build script for publish
@ -35,6 +45,8 @@ jobs:
-Platform:$(BuildPlatform)
-Configuration:$(BuildConfiguration)
-AppPlatform:uwp
-ReleaseVersion:"$(semanticVersion)"
-FileVersion:"$(fileVersion)"
- script: echo TODO - Add Tests here
displayName: '[Test] - To be Added via bug #77'

Просмотреть файл

@ -1,3 +1,9 @@
# The release branch versions start with the release number
# such as "0.69." or "0.70." and follow the revision number.
# name: 0.70.$(Rev:r)
#
# The main branch pre-release versions start with "0.0."
# and follow the date and revision number.
name: 0.0.$(Date:yyMM.d)$(Rev:rrr)
pr: none
@ -17,4 +23,12 @@ variables:
value: production,externalfacing
jobs:
- job: Setup
steps:
- script: node .ado/scripts/setVersionNumber.js
name: setVersions
displayName: Compute version numbers
- template: jobs.yml
parameters:
dependsOnSetup: true

Просмотреть файл

@ -21,6 +21,12 @@ param(
# e.g. "10.0.17763.0"
[String]$SDKVersion = "",
# e.g. "0.0.0-2209.28001-8af7870c" for pre-release or "0.70.2" for release
[String]$ReleaseVersion = "",
# e.g. "0.0.2209.28001" for pre-release or "0.70.2.0" for release
[String]$FileVersion = "",
[switch]$RunTests,
[switch]$Incremental,
[switch]$UseVS,
@ -112,6 +118,34 @@ function Invoke-Environment($Command, $arg) {
}}
}
function Invoke-UpdateReleaseVersion($SourcesPath, $ReleaseVersion, $FileVersion) {
if ([String]::IsNullOrWhiteSpace($ReleaseVersion)) {
return
}
$ProjectVersion = $ReleaseVersion
if ($ReleaseVersion.StartsWith("0.0.0")) {
# Use file version as a project version for pre-release builds
# because CMake does not accept our pre-release version format.
$ProjectVersion = $FileVersion
}
$filePath1 = Join-Path $SourcesPath "CMakeLists.txt"
$versionRegex1 = ' VERSION .*'
$versionStr1 = ' VERSION ' + $ProjectVersion
$content1 = (Get-Content $filePath1) -replace $versionRegex1, $versionStr1 -join "`r`n"
[IO.File]::WriteAllText($filePath1, $content1)
$filePath2 = Join-Path (Join-Path $SourcesPath "npm") "package.json"
$versionRegex2 = '"version": ".*",'
$versionStr2 = '"version": "' + $ReleaseVersion + '",'
$content2 = (Get-Content $filePath2) -replace $versionRegex2, $versionStr2 -join "`r`n"
[IO.File]::WriteAllText($filePath2, $content2)
Write-Host "Release version set to $ReleaseVersion"
Write-Host "Project version set to $ProjectVersion"
}
function get-CommonArgs($Platform, $Configuration, $AppPlatform, [ref]$genArgs) {
if ($UseVS.IsPresent) {
# TODO: use VS version chosen before
@ -130,6 +164,12 @@ function get-CommonArgs($Platform, $Configuration, $AppPlatform, [ref]$genArgs)
$genArgs.Value += '-DHERMESVM_PLATFORM_LOGGING=On'
$genArgs.Value += '-DHERMESJSI_DISABLE_STATS_TIMER=On'
if (![String]::IsNullOrWhiteSpace($FileVersion)) {
$genArgs.Value += '-DHERMES_FILE_VERSION=' + $FileVersion
}
Write-Host "HERMES_FILE_VERSION is $FileVersion"
}
function Invoke-BuildImpl($SourcesPath, $buildPath, $genArgs, $targets, $incrementalBuild, $Platform, $Configuration, $AppPlatform) {
@ -425,6 +465,8 @@ if (!(Test-Path -Path $WorkSpacePath)) {
Push-Location $WorkSpacePath
try {
Invoke-UpdateReleaseVersion -SourcesPath $SourcesPath -ReleaseVersion $ReleaseVersion -FileVersion $FileVersion
# run the actual builds and copy artefacts
foreach ($Plat in $Platform) {
foreach ($Config in $Configuration) {

Просмотреть файл

@ -0,0 +1,82 @@
/*
* This script sets the version number for the rest of the build.
* After this script has run, other tasks can use the variable
* to retrieve the build number for their logic...
*
* See: https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml
* for environment variables used in this script to compute the version number.
*/
const env = process.env;
function main() {
if (env["Build_Reason"] === "PullRequest") {
fatalError("Build script is intended for CI pipeline and should not be used for pull requests.");
}
const {semanticVersion, fileVersion} = computeVersion();
console.log(`Semantic Version: ${semanticVersion}`);
console.log(`Windows File Version: ${fileVersion}`);
if (!fileVersion.startsWith(semanticVersion)) {
// Update the pipeline build number to correlate it with the semantic version.
console.log(`##vso[build.updatebuildnumber]${fileVersion} -- ${semanticVersion}`);
}
// Set the variables (as output) so that other jobs can use them.
console.log(`##vso[task.setvariable variable=semanticVersion;isOutput=true]${semanticVersion}`);
console.log(`##vso[task.setvariable variable=fileVersion;isOutput=true]${fileVersion}`);
}
function computeVersion() {
// Compute base version;
const sourceBranch = env["Build_SourceBranch"];
if (sourceBranch === "refs/heads/main") {
return computeMainVersion();
}
if (sourceBranch.startsWith("refs/heads/rnw/0.")) {
return computeReleaseVersion();
}
fatalError(`Build script does not support source branch '${sourceBranch}'.`)
}
function computeMainVersion() {
const buildNumber = env["Build_BuildNumber"];
const buildNumberParts = buildNumber.split(".");
if (buildNumberParts.length !== 4
|| buildNumberParts[0] !== '0'
|| buildNumberParts[1] !== '0'
|| buildNumberParts[2].length !== 4
|| buildNumberParts[3].length < 4
|| buildNumberParts[3].length > 5) {
fatalError(`Unexpected pre-release build number format encountered: ${buildNumber}`)
}
const shortGitHash = env["Build_SourceVersion"].substring(0, 8);
return {
semanticVersion: `0.0.0-${buildNumberParts[2]}.${buildNumberParts[3]}-${shortGitHash}`,
fileVersion: buildNumber
}
}
function computeReleaseVersion() {
const buildNumber = env["Build_BuildNumber"];
const buildNumberParts = buildNumber.split(".");
if (buildNumberParts.length !== 3) {
fatalError(`Unexpected release build number format encountered: ${buildNumber}`)
}
return {
semanticVersion: buildNumber,
fileVersion: buildNumber + '.0'
}
}
function fatalError(message) {
console.log(`##[error]${message}`);
process.exit(1);
}
main();

Просмотреть файл

@ -9,6 +9,6 @@ set(HERMES_ENABLE_RTTI ON)
add_subdirectory(napi)
add_subdirectory(hermes)
if (HERMES_ENABLE_DEBUGGER)
if (WIN32 AND HERMES_ENABLE_DEBUGGER)
add_subdirectory(inspector)
endif()

Просмотреть файл

@ -51,15 +51,12 @@ add_hermes_library(compileJS STATIC CompileJS.cpp LINK_LIBS hermesPublic)
set(HERMES_ENABLE_EH ON)
set(HERMES_ENABLE_RTTI ON)
set(HERMES_VER_MAJOR ${PROJECT_VERSION_MAJOR})
set(HERMES_VER_MINOR ${PROJECT_VERSION_MINOR})
set(HERMES_VER_BUILD ${PROJECT_VERSION_PATCH})
set(HERMES_VER_REVISION ${VERSION_SUFFIX})
add_library(libhermes SHARED ${api_sources})
configure_file(version.rc.in version.rc @ONLY)
add_library(libhermes SHARED ${api_sources}
${CMAKE_CURRENT_BINARY_DIR}/version.rc)
if (WIN32)
configure_file(version.rc.in version.rc @ONLY)
target_sources(libhermes PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
endif()
# This is configured using a cmake flag instead of a separate target, because
# we need the output to be named "libhermes.so".

Просмотреть файл

@ -1,8 +1,8 @@
#include <verrsrc.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION @HERMES_VER_MAJOR@,@HERMES_VER_MINOR@,@HERMES_VER_BUILD@,0
PRODUCTVERSION @HERMES_VER_MAJOR@,@HERMES_VER_MINOR@,@HERMES_VER_BUILD@,0
FILEVERSION @HERMES_FILE_VERSION_BIN@
PRODUCTVERSION @HERMES_FILE_VERSION_BIN@
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -17,13 +17,12 @@ BEGIN
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "Hermes JavaScript Engine"
VALUE "FileVersion", "@HERMES_VER_MAJOR@.@HERMES_VER_MINOR@.@HERMES_VER_BUILD@.@HERMES_VER_REVISION@"
VALUE "FileDescription", "Hermes JavaScript Engine Library"
VALUE "FileVersion", "@HERMES_FILE_VERSION@"
VALUE "InternalName", "hermes.dll"
VALUE "OriginalFilename", "hermes.dll"
VALUE "ProductName", "Hermes JavaScript Engine"
VALUE "ProductVersion", "@HERMES_VER_MAJOR@.@HERMES_VER_MINOR@.@HERMES_VER_BUILD@.@HERMES_VER_REVISION@"
VALUE "ProductShortName", "Hermes"
VALUE "ProductVersion", "@HERMES_RELEASE_VERSION@"
END
END
BLOCK "VarFileInfo"

Просмотреть файл

@ -108,7 +108,15 @@ file(GLOB inspector_public_headers ${PROJECT_SOURCE_DIR}/public/hermes/Public/*.
set(HERMES_ENABLE_EH ON)
set(HERMES_ENABLE_RTTI ON)
add_library(hermesinspector SHARED ${inspector_sources} ${inspector_headers} ${folly_sources} ${inspector_public_headers})
configure_file(version.rc.in version.rc @ONLY)
add_library(hermesinspector SHARED
${inspector_sources}
${inspector_headers}
${folly_sources}
${inspector_public_headers}
${CMAKE_CURRENT_BINARY_DIR}/version.rc)
target_link_libraries(hermesinspector
hermesPublic
jsi

Просмотреть файл

@ -0,0 +1,32 @@
#include <verrsrc.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION @HERMES_FILE_VERSION_BIN@
PRODUCTVERSION @HERMES_FILE_VERSION_BIN@
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "Hermes JavaScript Engine Inspector"
VALUE "FileVersion", "@HERMES_FILE_VERSION@"
VALUE "InternalName", "inspector.dll"
VALUE "OriginalFilename", "inspector.dll"
VALUE "ProductName", "Hermes JavaScript Engine"
VALUE "ProductVersion", "@HERMES_RELEASE_VERSION@"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

Просмотреть файл

@ -62,7 +62,7 @@ endif()
# - npm/package.json
# - hermes-engine.podspec
project(Hermes
VERSION 0.71.0.20220908
VERSION 0.12.0
LANGUAGES C CXX)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/")
@ -78,6 +78,15 @@ if(NOT DEFINED HERMES_RELEASE_VERSION)
set(HERMES_RELEASE_VERSION ${PROJECT_VERSION})
endif()
# The version that we put inside of DLL files
if(NOT DEFINED HERMES_FILE_VERSION)
set(HERMES_FILE_VERSION "${PROJECT_VERSION}.0")
endif()
# The file version convertible to number representation.
# We must replace dots with commas.
string(REPLACE "." "," HERMES_FILE_VERSION_BIN ${HERMES_FILE_VERSION})
find_package(Python COMPONENTS Interpreter)
if (NOT Python_Interpreter_FOUND)
message(FATAL_ERROR "Unable to find Python interpreter, required for builds and testing.
@ -326,6 +335,11 @@ if(HERMES_RELEASE_VERSION)
add_definitions(-DHERMES_RELEASE_VERSION="${HERMES_RELEASE_VERSION}")
endif()
# Make the HERMES_FILE_VERSION accessible for version printing in C++.
if(HERMES_FILE_VERSION)
add_definitions(-DHERMES_FILE_VERSION="${HERMES_FILE_VERSION}")
endif()
if(HERMES_ENABLE_IR_INSTRUMENTATION)
add_definitions(-DHERMES_ENABLE_IR_INSTRUMENTATION)
endif()

48
doc/WindowsVersioning.md Normal file
Просмотреть файл

@ -0,0 +1,48 @@
---
id: hermes-windows-versioning
title: Versioning of hermes-windows
---
## Overview
In this document we outline the [microsoft/hermes-windows](https://github.com/microsoft/hermes-windows)
repo branches and the release versioning.
## Branches
- `main` - contains the latest code. Currently we manually sync it with
Meta hermes code.
- `rnw/*` - the folder with release branches
- `rnw/0.70-stable` - targets React Native for Windows (RNW) 0.70 release.
- `rnw/0.69-stable` - targets React Native for Windows (RNW) 0.69 release.
- `rnw/0.68-stable` - targets React Native for Windows (RNW) 0.68 release.
- `meta/*` - the folder with `main` and release `rn/*` branches from [facebook/hermes](https://github.com/facebook/hermes)
repo. We have a nightly process that pulls code to these branch. They have
no other changes. The goal for these branches is to see the integration
history into the `main` branch.
## Release versions
We have the following versioning schema for the `hermes-windows` releases:
- The **pre-release package versions** built in the `main` branch look like
**0.0.0-2209.9002-8af7870c** where the `<major>.<minor>.<patch>` versions are
always `'0.0.0'`, and the prerelease part that follows after `'-'` is
`'yyMM.drrr-hhhhhhhh'`. Where `'yy'` are two numbers for the year, `'MM'` are
two numbers for the month, `'d'` is the day without `0` prefix, `'rrr'` is a
three digit number for the today's revision, and `'hhhhhhhh'` are the first 8
hexadecimal numbers from the source GitHub commit hash.
- The **pre-release file versions** look like `0.0.2209.9002` where the the
encoding is `'0.0.yyMM.drrr'`. Where numbers after `'0.0.'` have the same
encoding as for the pre-release package version.
- The **released package versions** use the usual semantic schema which is
based on RNW release numbers like `0.70.1`, where the `'0.70'` is the
`<major>.<minor>` release of RNW and RN, and the last number is a `'patch'`
number for that release. Note that the `'patch'` number will not match the
`'patch'` number of the RNW. We are going to generate the `'patch'` number
using ADO build revision with format `'r'` that avoids `0` prefixes for the
version to be valid semantic version.
- The `release file versions` look like `0.70.1.0` to match the version of the package. The last part of the file version number is always `0`.

Просмотреть файл

@ -1,5 +1,5 @@
{
"version": "0.71.0.20220908",
"version": "0.12.0",
"scripts": {
"unpack-builds": "node unpack-builds.js",
"unpack-builds-dev": "node unpack-builds.js --dev",

Просмотреть файл

@ -47,6 +47,11 @@ target_link_libraries(hermes
${LIBREADLINE}
)
if (WIN32)
configure_file(version.rc.in version.rc @ONLY)
target_sources(hermes PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
endif()
install(TARGETS hermes
RUNTIME DESTINATION bin
)

Просмотреть файл

@ -0,0 +1,32 @@
#include <verrsrc.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION @HERMES_FILE_VERSION_BIN@
PRODUCTVERSION @HERMES_FILE_VERSION_BIN@
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "Hermes JavaScript Engine CLI"
VALUE "FileVersion", "@HERMES_FILE_VERSION@"
VALUE "InternalName", "hermes.exe"
VALUE "OriginalFilename", "hermes.exe"
VALUE "ProductName", "Hermes JavaScript Engine"
VALUE "ProductVersion", "@HERMES_RELEASE_VERSION@"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END