Below is an updated version of your README in markdown that reintroduces the details on how CFBundleVersion and versionCode are determined, along with the improvements made earlier. Let me know if you need any further adjustments.
A tool that generates package versions based on Git information. It can be used as a CLI, a GitHub Action, or as a direct dependency in your code.
The version generator creates version strings using Git data following the schema:
<major>.<minor>.<patch>-<branch-name>.<commit-hash>
For example:
v1.2-feature-branch.4-g5678abc
The tool extracts the version components as follows:
Component | Description | Source |
---|---|---|
major | Major version number | Extracted from the latest Git tag (e.g., v1.2 → 1 ) |
minor | Minor version number | Extracted from the latest Git tag (e.g., v1.2 → 2 ) |
patch | Patch version number | Number of commits since the latest tag |
branch-name | Current branch name | Git branch name (cleaned of special characters) |
commit-hash | Short commit hash | Short hash of the current commit |
Important: The tool requires at least one Git tag (formatted as
v<major>.<minor>
) to exist in your repository. Without a tag, the action will fail with an error message.
This approach automatically creates versions based on commit history and allows you to quickly trace back any commit from a version string. It also helps identify which branch the code originates from.
To update the major or minor version, create a new tag in the format:
v<major>.<minor>
For example:
git tag -a v1.2 -m "Release version 1.2"
git push origin v1.2
-
Service Versioning: Embed the version string in your service (e.g., under
/version.json
) to quickly identify the deployed version. - Artifact Versioning: Use the version string to tag deployment artifacts, ensuring clarity on which package version is being deployed.
For app packages, both iOS and Android have specific versioning requirements.
-
CFBundleShortVersionString: Represents the version of the app in the store. The App Store uses this string (formatted as
<major>.<minor>.<patch>
) to determine release ordering. Use theappReleaseVersion
produced by the tool. - CFBundleVersion: Ensures uniqueness between builds with identical version strings. This string can consist of up to three integers separated by periods.
- Android version strings are public and ideally follow the
<major>.<minor>.<patch>
format. Use theappReleaseVersion
produced by the tool. - Android does not use the version string for release ordering. Instead, it relies on a monotonically increasing integer called
versionCode
.
-
iOS:
The tool queries App Store Connect to determine the current build number for the given version string. If that version string does not yet exist in the App Store or TestFlight, the build number defaults to1
. The commit hash is then encoded as an integer, and these two values are combined into aniosBuildNumberString
using the format:<buildNumber>.<encoded-commitHash>
The first value ensures monotonically increasing build numbers for release ordering, while the second value allows you to decode and retrieve the commit hash. This
iosBuildNumberString
is used to set theCFBundleVersion
. -
Android:
The tool queries the Google Play Store API to retrieve the latest build version code used across all tracks and then increments it by one. Additionally, if the major version of the package in the store is different from the package being built, the version code is increased by a configurable increment (default: 10). The resultingandroidVersionCode
is used to set theversionCode
in your Android app.
Note: To generate values specific to iOS or Android, use the appropriate CLI flags or GitHub Action inputs.
The version generator normalizes environment variables from different CI/CD systems to ensure consistent behavior. You can explicitly set these variables or let the tool automatically detect them from your CI environment.
Variable | Description | Sources (in priority order) |
---|---|---|
TOKEN |
GitHub access token |
GITHUB_TOKEN , TRAVIS_TOKEN
|
REPOSITORY_OWNER |
Repository owner/organization |
GITHUB_REPOSITORY_OWNER , VERCEL_GIT_REPO_OWNER , TRAVIS_REPO_SLUG (parsed) |
REPOSITORY_NAME |
Repository name (without owner) |
GITHUB_REPOSITORY (parsed), VERCEL_GIT_REPO_SLUG , TRAVIS_REPO_SLUG (parsed) |
SHA |
Commit hash |
GITHUB_SHA , VERCEL_GIT_COMMIT_SHA , TRAVIS_COMMIT
|
BRANCH_NAME |
Current branch name |
GITHUB_HEAD_REF (PR source), GITHUB_REF_NAME , TRAVIS_PULL_REQUEST_BRANCH (PR source), TRAVIS_BRANCH , VERCEL_GIT_COMMIT_REF
|
CI |
Indicates running in CI environment |
GITHUB_ACTIONS , VERCEL , TRAVIS , CI
|
The tool automatically detects and normalizes environment variables from:
-
GitHub Actions: Uses
GITHUB_*
environment variables -
Vercel: Uses
VERCEL_*
environment variables -
Travis CI: Uses
TRAVIS_*
environment variables - Generic CI: Uses expected environment variable directly
Note: When running in a CI environment, a GitHub token (
TOKEN
) is required for accessing the GitHub API. This is automatically available in GitHub Actions asGITHUB_TOKEN
, but must be manually set in other CI systems.
You can also set these normalized environment variables directly in your environment, and they will be used as-is without further normalization:
# Example of directly setting normalized variables
export TOKEN="your-github-token"
export REPOSITORY_OWNER="your-username"
export REPOSITORY_NAME="your-repo"
export SHA="abcdef1234567890"
export BRANCH_NAME="main"
export CI="true"
You can install the version generator globally or as a dev dependency.
Add the package to your project:
npm add -d @wellsite/version-generator
To display help:
# npm
npx @wellsite/version-generator --help
# pnpm
pnpm dlx @wellsite/version-generator --help
-
--dir, -d
: Directory to use for command execution and output file path (defaults to current working directory) -
--output-file
: Output file path (relative to --dir if not absolute) where the version file should be written. Can be specified multiple times to write to multiple locations. -
--format, -f
: Output format:string
orjson
(default:string
) -
--android
: Enable Android version code generation -
--android-package
: Android package name for the Play Store API -
--android-service-account-key
: Service account key for the Play Store API (JSON string or base64-encoded) -
--android-track
: Track for the Play Store API (production, beta, alpha; default: production) -
--android-major-increment
: Increment for major version changes (default: 10) -
--ios
: Enable iOS build number generation -
--ios-bundle-id
: iOS bundle ID for the App Store Connect API -
--ios-api-key-id
: App Store Connect API Key ID -
--ios-api-issuer-id
: App Store Connect API Issuer ID -
--ios-api-private-key
: App Store Connect API Private Key (can be base64-encoded)
Local Usage: The tool uses local Git commands and does not require a
GITHUB_TOKEN
.
GitHub Actions: When running in a GitHub Actions environment (GITHUB_ACTIONS=true
), the tool uses the GitHub API and requiresGITHUB_TOKEN
.
Basic usage to output version to console:
npx @wellsite/version-generator
Write version to a file:
npx @wellsite/version-generator --output-file version.json --format json
Write version to multiple files:
npx @wellsite/version-generator --output-file version.json --output-file public/version.json --format json
When using the JSON format, the output includes additional metadata:
{
"version": "1.2.4-g5678abc",
"major": "1",
"minor": "2",
"patch": 4,
"branchName": "main",
"commitHash": "5678abc",
"appReleaseVersion": "1.2.4",
"androidVersionCode": 15,
"iosBuildNumberString": "1.23234323"
}
Here’s an example workflow step:
- name: Generate version
uses: wellsite/version-generator@v1
with:
outputFilePath: 'version.json'
format: 'json'
android: 'true'
android-package: 'com.example.app'
android-service-account-key: ${{ secrets.PLAY_STORE_SERVICE_ACCOUNT_KEY_BASE64 }}
android-track: 'production'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Input | Description | Required | Default |
---|---|---|---|
dir |
Directory to use for command execution and output file path | No | Current dir |
destination |
Destination path (relative to dir if not absolute) | No | – |
format |
Output format (string or json ) |
No | string |
android |
Enable Android version code generation | No | false |
android-package |
Android package name for the Play Store API | No | – |
android-service-account-key |
Service account key for the Play Store API (as JSON string) | No | – |
android-track |
Track for the Play Store API (production, beta, alpha) | No | production |
android-major-increment |
Increment for major version changes | No | 10 |
ios |
Enable iOS build number generation | No | false |
ios-bundle-id |
iOS bundle ID for the App Store Connect API | No | – |
ios-api-key-id |
App Store Connect API Key ID | No | – |
ios-api-issuer-id |
App Store Connect API Issuer ID | No | – |
ios-api-private-key |
App Store Connect API Private Key (can be base64-encoded) | No | – |
-
version
: The generated version string. -
major
: Major version number. -
minor
: Minor version number. -
patch
: Patch version number (commit count since the last tag). -
branchName
: Current branch name. -
commitHash
: Current commit hash. -
appReleaseVersion
: App release version (only major.minor.patch) for mobile apps. -
androidVersionCode
: Android version code (if Android input is enabled). -
iosBuildNumber
: iOS build number (if iOS input is enabled).
You can also use the version generator directly in your code.
pnpm add @wellsite/version-generator
const { generatePackageVersion, generateAndWriteVersion } = require('@wellsite/version-generator');
// Generate version information without writing to a file
const versionInfo = await generatePackageVersion('.');
console.log(versionInfo);
// Generate version information with Android version code
const versionInfoWithAndroid = await generatePackageVersion('.', {
android: {
enabled: true,
packageName: 'com.example.app',
serviceAccountKey: '{ ... }', // Service account key JSON
track: 'production',
majorVersionIncrement: 10
}
});
console.log(versionInfoWithAndroid);
// Generate version information with iOS build number
const versionInfoWithIos = await generatePackageVersion('.', {
ios: {
enabled: true,
bundleId: 'com.example.app',
apiKeyId: 'XXXXXXXXXX',
apiIssuerId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
apiPrivateKey: '-----BEGIN PRIVATE KEY-----\n...', // Can also be base64-encoded
}
});
console.log(versionInfoWithIos);
// Alternatively, generate and write to a file
const writtenVersionInfo = await generateAndWriteVersion('.', 'version.json');
console.log(writtenVersionInfo);
The returned object includes properties such as:
{
"version": "1.0.0-main.12345abc",
"appReleaseVersion": "1.0.0",
"iosBuildNumberString": "42",
"iosBuildNumber": 42,
// ...other properties
}
The tool can automatically generate Android version codes by:
- Creating a Service Account: In the Google Cloud Console with access to the Google Play Android Developer API.
- Downloading the Service Account Key JSON.
-
Encoding the Key: For GitHub Actions, encode the JSON as base64:
cat service-account-key.json | base64
- Storing the Key: Add the base64-encoded string as a GitHub secret.
- Inviting the Service Account: Invite the service account (via its email) to your Google Play Console.
The tool automatically generates iOS build numbers by:
- Creating an API Key in App Store Connect.
-
Collecting Credentials:
-
Key ID: Displayed after generating the key (e.g.,
ABC1234567
). - Issuer ID: Found at the top of the Keys page.
-
Private Key: Download the
.p8
file (downloadable only once). -
Bundle ID: Your app's bundle identifier (e.g.,
com.example.app
).
-
Key ID: Displayed after generating the key (e.g.,
-
Preparing the Private Key: Encode it as base64:
cat AuthKey_XXXXXXXX.p8 | base64
- Storing Securely: For GitHub Actions, store the Key ID, Issuer ID, and base64-encoded private key as secrets. Never commit these values to your repository.
If you see the error:
No git tags found fatal: No names found, cannot describe anything
It means your repository lacks any Git tags. The version generator requires at least one tag to function.
Solution:
Create an initial tag manually:
git tag -a v0.0.0 -m "Initial version"
git push origin v0.0.0
Ensure that your repository has at least one tag. Note that the action is configured to work with shallow clones (e.g., fetch-depth: 1
), which may affect commit count retrieval.
For more information on contributing and developing this project, please see DEVELOPMENT.md.
This project uses tags to version the Github Action. Although we use our own version generator to version the published artifacts, we also tag the commit in the main branch with the major version during the build. This enables users of our GitHub Action to pin the version of the action to the latest major version of the build. For example:
uses: Wellsite-Navigator/version-generator@v1 # Uses the latest v1.x.x-main.xxx release via the v1 tag
The @v1
syntax is shorthand for always using the latest release in the v1
major version series from the main branch.
This project is licensed under the MIT License.