Skip to content

Commit

Permalink
Merge pull request #165 from microbiomedata/issue-159-version-and-bui…
Browse files Browse the repository at this point in the history
…ld-numbers

Rework release process to account for version and build numbers
  • Loading branch information
pkalita-lbl authored Aug 28, 2024
2 parents d9f9588 + 6286fa8 commit 8ef6f85
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 71 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ jobs:
- uses: actions/checkout@v4
- name: Create Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
75 changes: 38 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,33 +232,40 @@ Here's how you can introduce a new environment variable to the code base:

### Make a release

Creating a release involves three steps: creating a new version tag and GitHub Release, creating and distributing a new Android build, and creating and distributing a new iOS build.
Creating a release involves three steps:

#### Create Version Tag and GitHub Release
- Increment the build number and optionally update the version number
- Create and distribute a new Android build
- Create and distribute a new iOS build

1. Ensure you are on the main branch and have the latest changes.
```shell
git checkout main && git pull
```
2. Run a test build. Ensure that this command completes successfully and leaves _no local changes_. If there are local changes, commit them separately before proceeding.
```shell
ionic capacitor sync --prod
```
3. Decide whether the new version will be a patch, minor, or major version. Then, run the following commands to create a new version commit and tag.
#### Incrementing build and version numbers

> [!NOTE]
> During the beta testing period, use only minor or patch versions.
**Decide**: Are you releasing a new **build** or a new **version**?

```shell
npm version patch # (or "minor" or "major")
git push && git push --tags
```
<details>
<summary>How do I choose?</summary>

- A build release is a new **testing** release of the app. It may be thought of as a release candidate. It implies a new `buildNumber` in `package.json`. This corresponds to a new "build number" in [iOS terms](https://help.apple.com/xcode/mac/current/#/devba7f53ad4) or "versionCode" in [Android terms](https://developer.android.com/studio/publish/versioning). New builds (of already-released _versions_) do not go through Apple's approval process when released to TestFlight.
- A version release is a new **general** release of the app. It implies a new `version` number in `package.json` _and_ new `buildNumber`. The commit where these numbers are updated is also tagged. The new `version` corresponds to a new "version number" in [iOS terms](https://help.apple.com/xcode/mac/current/#/devba7f53ad4) or "versionName" in [Android terms](https://developer.android.com/studio/publish/versioning). There will typically be several build releases before a version release.

</details>

4. Check [GitHub Actions](https://github.com/microbiomedata/nmdc-field-notes/actions) to ensure that pushing the version tag triggered the workflow that creates a new GitHub Release, and that that workflow completed successfully.
5. If proceeding to create Android or iOS builds, run another production build.
1. Ensure you are on the main branch and have the latest changes.
```shell
ionic capacitor sync --prod
git checkout main && git pull
```
2. Update build and (optionally) version numbers.
1. If this is a **build** release, run the following commands to increment the build number.
```shell
npm run release -- build
git push
```
2. If this is a **version** release, decide whether the new version will be a patch, minor, or major version. Then, run the following commands to create a new version commit and tag. **NOTE**: During the beta testing period, use only minor or patch versions.
```shell
npm run release -- patch # (or "minor" or "major")
git push --follow-tags
```
3. If this is a **version** release, check [GitHub Actions](https://github.com/microbiomedata/nmdc-field-notes/actions) to ensure that pushing the version tag triggered the workflow that creates a new GitHub Release. Ensure that the workflow completed successfully.

#### Create and Distribute a new Android Build

Expand All @@ -281,12 +288,7 @@ Creating a release involves three steps: creating a new version tag and GitHub R

</details>

1. If you are **not** continuing from the version tag section, checkout the version tag and run a production build.
```shell
git checkout vX.Y.Z # replace with the version tag
ionic capacitor sync --prod
```
2. Build the Android APK file.
1. Build the Android APK file.
1. Open the Android project in Android Studio.
```shell
ionic capacitor open android
Expand All @@ -300,24 +302,23 @@ Creating a release involves three steps: creating a new version tag and GitHub R
- Key password: `<keystore password>`
5. Select the "release" build variant and click "Create"
6. Wait for the gradle build to complete. Look for notification saying "Build completed successfully for module 'android.app.main' with 1 build variant."
3. Distribute the APK file via the GitHub Release.
1. Make an appropriately named copy of the APK file.
2. Distribute the APK file via the GitHub Release.
1. Make a copy of the APK file with the version and build numbers in the filename.
```shell
npm run rename.apk
```
2. Edit the vX.Y.Z [GitHub Release](https://github.com/microbiomedata/nmdc-field-notes/releases) and attach the `org.microbiomedata.fieldnotes-vX.Y.Z-build.N.apk` file to it. **NOTE**: If this is a **build** release there may be one or more existing APK files attached to the release. This is by design.
3. Deleted the APK file from your local project root.
```shell
cp android/app/release/app-release.apk org.microbiomedata.fieldnotes-vX.Y.Z.apk # replace with version number
rm org.microbiomedata.fieldnotes-*.apk
```
2. Edit the vX.Y.Z [GitHub Release](https://github.com/microbiomedata/nmdc-field-notes/releases) and attach the `org.microbiomedata.fieldnotes-vX.Y.Z.apk` file to it.

#### Create and Distribute a new iOS Build

> [!NOTE]
> These instructions are based around distributing via TestFlight for the beta release period. These will be updated later to include App Store distribution once a stable release is ready.

1. If you are **not** continuing from the version tag section, checkout the version tag and run a production build.
```shell
git checkout vX.Y.Z # replace with the version tag
ionic capacitor sync --prod
```
2. Create the iOS build:
1. Create the iOS build:
1. Open the iOS project in Xcode.
```shell
ionic capacitor open ios
Expand All @@ -326,7 +327,7 @@ Creating a release involves three steps: creating a new version tag and GitHub R
3. "[Select the method of distribution](https://developer.apple.com/documentation/xcode/distributing-your-app-for-beta-testing-and-releases#Select-a-method-for-distribution)" to be TestFlight & App Store
4. Click the "Validate App" button to validate the build with respect to App Store Connect
5. Click the "Distribute App" button to upload the build to App Store Connect
3. Distribute the iOS build via TestFlight:
2. Distribute the iOS build via TestFlight:
1. Log in to [App Store Connect](https://appstoreconnect.apple.com/)
2. Go to `Apps` > `NMDC Field Notes` > `TestFlight`
3. Under "iOS Builds," find the newly-uploaded build (in the table of all builds)
Expand Down
9 changes: 0 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
"name": "nmdc-field-notes",
"private": true,
"version": "0.1.0",
"config": {
"buildNumber": 4
},
"type": "module",
"scripts": {
"dev": "ionic serve --host=127.0.0.1 --port=8100 --no-open",
"build": "tsc && vite build",
"build.native": "tsc && ionic capacitor sync",
"format": "prettier . --write",
"check.format": "prettier . --check",
"check.imports": "madge --circular src/main.tsx",
Expand All @@ -17,8 +21,9 @@
"lint": "eslint src",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"preversion": "vitest run",
"version": "APP_VERSION=$npm_package_version trapeze run trapeze.config.yaml -y && git add android ios"
"prerelease": "vitest run && npm run build.native",
"release": "./scripts/release.sh",
"rename.apk": "cp android/app/release/app-release.apk org.microbiomedata.fieldnotes-v$npm_package_version-build.$npm_package_config_buildNumber.apk"
},
"dependencies": {
"@capacitor/android": "^6.0.0",
Expand Down Expand Up @@ -91,7 +96,6 @@
"terser": "^5.4.0",
"typescript": "^5.1.6",
"vite": "^5.0.0",
"vite-plugin-package-version": "^1.1.0",
"vitest": "^0.34.6"
},
"optionalDependencies": {
Expand Down
69 changes: 69 additions & 0 deletions scripts/release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env bash

set -e

log() {
echo "[release] $1"
}

# Validate the first CLI option passed to the script.
if [[ ! $1 =~ ^(major|minor|patch|build)$ ]]; then
log "ERROR: Must specify a release type: major, minor, patch, or build"
exit 1
fi

RELEASE_TYPE=$1

log "Checking for clean working directory"
if [[ -n "$(git status --porcelain)" ]]; then
log "ERROR: Working directory must be clean. Stash or commit changes before releasing."
exit 1
fi

# Increment the build number by getting the current build number from the package.json file,
# incrementing it by 1, and then setting the new build number in the package.json file.
CURRENT_BUILD_NUMBER=${npm_package_config_buildNumber:=}
if [[ -z $CURRENT_BUILD_NUMBER ]]; then
log "ERROR: Cannot determine current build number from package.json"
log " Ensure that release.sh is being called via 'npm run release' and not directly"
exit 1
fi
NEW_BUILD_NUMBER=$((CURRENT_BUILD_NUMBER + 1))
npm pkg set config.buildNumber=$NEW_BUILD_NUMBER --json

log "New build number: $NEW_BUILD_NUMBER"

# If this is a build release, grab the existing version number; otherwise, increment the version
# number by running 'npm version' and then grab the new version number.
if [[ "$RELEASE_TYPE" == "build" ]]; then
VERSION_NUMBER=${npm_package_version:=}
if [[ -z $VERSION_NUMBER ]]; then
log "ERROR: Cannot determine current version number from package.json"
log " Ensure that release.sh is being called via 'npm run release' and not directly"
exit 1
fi
log "Keeping existing version number: $VERSION_NUMBER"
else
VERSION_NUMBER=$(npm version "$RELEASE_TYPE" --no-git-tag-version)
VERSION_NUMBER=${VERSION_NUMBER:1} # Remove the 'v' prefix
log "New version number: $VERSION_NUMBER"
fi

# Run trapeze to propagate the new version and build number to native projects
log "Updating native projects"
export FIELD_NOTES_VERSION_NUMBER=$VERSION_NUMBER
export FIELD_NOTES_BUILD_NUMBER=$NEW_BUILD_NUMBER
trapeze run trapeze.config.yaml -y

# Commit the changes to package.json, package-lock.json, and native projects
COMMIT_MESSAGE="$VERSION_NUMBER ($NEW_BUILD_NUMBER)"
log "Creating commit: $COMMIT_MESSAGE"
git add package.json package-lock.json android ios
git commit -m "$COMMIT_MESSAGE"

# If this is not a build release, tag the commit with the new version number
if [[ "$RELEASE_TYPE" != "build" ]]; then
TAG_MESSAGE="v$VERSION_NUMBER"
log "Creating tag: $TAG_MESSAGE"
git tag -a "$TAG_MESSAGE" -m "$TAG_MESSAGE"
fi
4 changes: 3 additions & 1 deletion src/components/SettingsAboutList/SettingsAboutList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ const SettingsAboutList: React.FC = () => {
<IonItem>
<IonLabel>
<h3>Version</h3>
<p>{config.APP_VERSION}</p>
<p>
{config.APP_VERSION} ({config.APP_BUILD})
</p>
</IonLabel>
</IonItem>
</IonList>
Expand Down
18 changes: 15 additions & 3 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
const env = import.meta.env;

interface Config {
APP_VERSION: typeof env.PACKAGE_VERSION;
APP_VERSION: typeof env.FIELD_NOTES_VERSION_NUMBER;
APP_BUILD: typeof env.FIELD_NOTES_BUILD_NUMBER;
NMDC_SERVER_API_URL: string;
SUPPORT_EMAIL: string;
}
Expand All @@ -22,9 +23,20 @@ const config: Config = {
/**
* Version identifier of the app (e.g. "1.23.456").
*
* This is the value of the `version` property in the `package.json` file.
* This is the value of the `version` property in the `package.json` file. However, it
* is only set when initiated by an NPM script (i.e. `npm run <script>`). If you are not
* seeing the correct value, confirm that you are using an NPM script.
*/
APP_VERSION: env.PACKAGE_VERSION,
APP_VERSION: env.FIELD_NOTES_VERSION_NUMBER || "0.0.0",

/**
* Build identifier of the app (e.g. "4").
*
* This is the value of the `config.buildNumber` property in the `package.json` file.
* However, it is only set when initiated by an NPM script (i.e. `npm run <script>`). If you
* are not seeing the correct value, confirm that you are using an NPM script.
*/
APP_BUILD: env.FIELD_NOTES_BUILD_NUMBER || "0",

/**
* URL of the endpoint the mobile app can use to access the NMDC data portal API.
Expand Down
3 changes: 2 additions & 1 deletion src/vite-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
interface ImportMetaEnv {
readonly VITE_NMDC_SERVER_API_URL?: string;
readonly VITE_NMDC_SERVER_LOGIN_URL?: string;
readonly PACKAGE_VERSION: string;
readonly FIELD_NOTES_BUILD_NUMBER: string;
readonly FIELD_NOTES_VERSION_NUMBER: string;
}

/**
Expand Down
12 changes: 6 additions & 6 deletions trapeze.config.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
vars:
APP_VERSION:
default: 0.0.0
FIELD_NOTES_VERSION_NUMBER:
FIELD_NOTES_BUILD_NUMBER:

platforms:
android:
incrementVersionCode: true
versionName: $APP_VERSION
versionCode: $FIELD_NOTES_BUILD_NUMBER
versionName: $FIELD_NOTES_VERSION_NUMBER
ios:
targets:
App:
incrementBuild: true
version: $APP_VERSION
buildNumber: $FIELD_NOTES_BUILD_NUMBER
version: $FIELD_NOTES_VERSION_NUMBER
26 changes: 15 additions & 11 deletions vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
/// <reference types="vitest" />
import legacy from "@vitejs/plugin-legacy";
import react from "@vitejs/plugin-react";
import packageVersion from "vite-plugin-package-version";
import { defineConfig } from "vite";

// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
legacy(),
/**
* Note: The `packageVersion()` plugin reads the `version` value from `package.json`
* and makes it available to the app via `import.meta.env.PACKAGE_VERSION`.
* Reference: https://www.npmjs.com/package/vite-plugin-package-version
*/
packageVersion(),
],
plugins: [react(), legacy()],
test: {
globals: true,
environment: "jsdom",
setupFiles: "./src/setupTests.ts",
},
define: {
// This makes the `version` value from `package.json` available to the app
// as `import.meta.env.FIELD_NOTES_VERSION_NUMBER`. This only works when
// invoked by an NPM script (i.e. `npm run <script>`).
"import.meta.env.FIELD_NOTES_VERSION_NUMBER": JSON.stringify(
process.env.npm_package_version,
),
// This makes the `config.buildNumber` value from `package.json` available to
// the app as `import.meta.env.FIELD_NOTES_BUILD_NUMBER`. This only works when
// invoked by an NPM script (i.e. `npm run <script>`).
"import.meta.env.FIELD_NOTES_BUILD_NUMBER": JSON.stringify(
process.env.npm_package_config_buildNumber,
),
},
});

0 comments on commit 8ef6f85

Please sign in to comment.