diff --git a/README.md b/README.md index 2e2b03991d2..aa3ee8e05b5 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,6 @@ Otherwise, to get an APK, do: ### Build release version of app -First, run `yarn update-version` to copy the `package.json` version into the native project files, and to assign a unique build number. - #### iOS - Open `edge-react-gui/ios/edge.xcworkspace` in Xcode @@ -98,18 +96,41 @@ First, run `yarn update-version` to copy the `package.json` version into the nat --- -## Deploying (macOS Only) +## Deploying (MacOS Only) + +This repo includes several utility scripts that can be used in a CI/CD +environment to build and deploy a release version of the app. The +included `Jenkinsfile` utilizes all the scripts but you are free to +use them in your own CI environment. + +### Update the version file + +Set `BUILD_REPO_URL` to the URL of an empty Git repo that will hold a version +file that will be auto updated to increment the version and build number. Then +run the following to update a local `release-version.json` file + + yarn gitVersionFile + +Update the project files based on the version in `release-version.json` + + yarn updateVersion + + +### Build, sign, and deploy -The included `deploy.js` is a script to automate building, signing, and deploying release builds of Edge. It provides the following: +The included `deploy.ts` is a script to automate building, signing, and deploying release builds of Edge. It provides the following: - Auto sign Android APK with Android keystore files - Auto sign iOS IPA with provisioning profiles +- Build release version of iOS and Android +- Upload iOS IPA and Android APK files to AppCenter for developer testing ### To Use -- Run `yarn update-version` to set up your build number & version. - Set the env var KEYCHAIN_PASSWORD to the keychain password of the current user - Copy the `deploy-config.sample.json` to `deploy-config.json` and edit the parameters accordingly. You'll need a HockeyApp account to get ids and keys +- Download a copy of the Google Bundle tool (https://github.com/google/bundletool/releases) +- Set the `bundleToolPath` in `deploy-config.json` to the path to the bundle tool `.jar` file - Put any Android keystore files into `edge-react-gui/keystores/` - If using Firebase, put your account's `google-services.json` and `GoogleService-Info.plist` into `edge-react-gui/` - Install xcpretty `sudo gem install xcpretty` @@ -121,6 +142,22 @@ yarn deploy edge ios master yarn deploy edge android master ``` +## Fastlane support + +This repo supports utilizing Fastlane to automate updates to iOS Provisioning +Profiles. To use Fastlane, set the following environment variables and run +`yarn deploy` as mentioned above + + BUILD_REPO_URL // Git repo used to store encrypted provisioning + // keys. + // Will be shared with the gitVersionFile.ts script + FASTLANE_USER // Apple ID email + FASTLANE_PASSWORD // Apple ID password + GITHUB_SSH_KEY // (Optional) SSH Key file to use when accessing + // BUILD_REPO_URL + MATCH_KEYCHAIN_PASSWORD // Password to unlock the current users keychain + MATCH_PASSWORD // Password used to encrypt profile information + // before being saved to the BUILD_REPO_URL --- ## Debugging diff --git a/deploy-config.sample.json b/deploy-config.sample.json index d3d0db706fd..cc33639e1e6 100644 --- a/deploy-config.sample.json +++ b/deploy-config.sample.json @@ -1,12 +1,13 @@ { "edge": { "productName": "Edge Wallet", + "xcodeProject": "edge.xcodeproj", "xcodeWorkspace": "edge.xcworkspace", "xcodeScheme": "edge", "androidKeyStore": "edge-release-keystore.jks", "androidKeyStoreAlias": "edge", "androidKeyStorePassword": "xxxxxxxxx", - "androidTask": "assembleRelease", + "androidTask": "bundleRelease", "hockeyAppToken": "xxxxx", "hockeyAppTags": "internal", "bugsnagApiKey": "xxxxxxxxxx", @@ -14,6 +15,9 @@ "appCenterGroupName": "xxxxxxxxxx", "appCenterDistroGroup": "xxxxxxxxxx", "appleDeveloperTeamId": "xxxxxxxxxx", + "appleDeveloperTeamName": "edge", + "bundleId": "co.edgesecure.app", + "bundleToolPath": "/Users/jenkins/bin/bundletool-all-1.11.2.jar", "ios": { "master": { "hockeyAppId": "xxxxxxxxx" diff --git a/package.json b/package.json index 2776a440a2e..c28953b9b0f 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "fix-java": "cd android; ./format-java.sh", "fix-swift": "swift-format format -i ios/*.swift", "fix": "npm run lint -- --fix && yarn-deduplicate", + "gitVersionFile": "node -r sucrase/register scripts/gitVersionFile.ts", "ios": "react-native run-ios", "lint": "eslint .", "localize": "node -r sucrase/register ./scripts/localizeLanguage.ts; git add ./src/locales/strings", @@ -49,7 +50,7 @@ "start": "react-native start", "test": "jest", "theme": "node -r sucrase/register ./scripts/themeServer.ts", - "update-version": "node -r sucrase/register scripts/updateVersion.ts", + "updateVersion": "node -r sucrase/register scripts/updateVersion.ts", "updot": "EDGE_MODE=development updot", "watch": "npm test -- --watch" }, diff --git a/scripts/gitVersionFile.ts b/scripts/gitVersionFile.ts index fbc1f2495b4..3271a9584ce 100644 --- a/scripts/gitVersionFile.ts +++ b/scripts/gitVersionFile.ts @@ -46,7 +46,7 @@ const versionFileName = 'release-version.json' async function main() { const cwd = join(__dirname, '..') - const [branch] = process.argv.slice(2) + const branch = process.argv[2] ?? 'master' // Determine the current version: const packageJson = JSON.parse(fs.readFileSync(join(cwd, 'package.json'), { encoding: 'utf8' })) diff --git a/scripts/updateVersion.ts b/scripts/updateVersion.ts index 38712e817f3..664386c3d40 100644 --- a/scripts/updateVersion.ts +++ b/scripts/updateVersion.ts @@ -24,83 +24,19 @@ import path from 'path' import { asVersionFile, VersionFile } from './cleaners' -const specialBranches: { [branch: string]: string } = { - develop: '-d', - master: '', - beta: '', - coinhub: '', - staging: '-rc', - test: '-t', - yolo: '-yolo', - 'test-cheddar': '-cheddar', - 'test-feta': '-feta', - 'test-gouda': '-gouda', - 'test-halloumi': '-halloumi', - 'test-paneer': '-paneer' -} - async function main() { const cwd = path.join(__dirname, '..') const disklet = makeNodeDisklet(cwd) - const [branch] = process.argv.slice(2) - - // Determine the current build number: - const build = Math.max(pickBuildNumber(), 1 + (await readLastBuildNumber(disklet))) - // Determine the current version: - const packageJson = JSON.parse(await disklet.getText('package.json')) - const version = `${packageJson.version}${pickVersionSuffix(branch)}` + const file = await disklet.getText('release-version.json') + const versionFile = asVersionFile(file) - // Write the vesion info file: - const versionFile = { - branch, - build, - version - } console.log(versionFile) - await disklet.setText('release-version.json', JSON.stringify(versionFile, null, 2)) // Update the native project files: await Promise.all([updateAndroid(disklet, versionFile), updateIos(cwd, versionFile)]) } -/** - * Pick a build number based on the current date. - */ -function pickBuildNumber(now: Date = new Date()) { - const year = now.getFullYear() - 2000 - const month = now.getMonth() + 1 - const day = now.getDate() - const counter = 1 - - return (year % 100) * 1000000 + month * 10000 + day * 100 + counter -} - -/** - * Pick a suffix to add to the package.json version. - */ -function pickVersionSuffix(branch?: string): string { - if (branch == null || branch === '') return '' - - const specialSuffix = specialBranches[branch] - if (specialSuffix != null) return specialSuffix - - return '-' + branch.replace(/[^0-9a-zA-Z]+/g, '-') -} - -/** - * Read the previous build number from release-version.json file. - */ -async function readLastBuildNumber(disklet: Disklet): Promise { - try { - const text = await disklet.getText('release-version.json') - const { build } = asVersionFile(text) - return build - } catch (e) { - return 0 - } -} - /** * Inserts the build information into the Android project files. */ diff --git a/scripts/updateVersions.ts b/scripts/updateVersions.ts deleted file mode 100644 index f9b526d1729..00000000000 --- a/scripts/updateVersions.ts +++ /dev/null @@ -1,58 +0,0 @@ -// This script sets up the version numbers for a release build. -// -// Run it as `node -r sucrase/register ./scripts/updateVersion.ts []` -// -// This script inserts the build number and version into the native project -// files, leaving the project ready for a release build. - -import childProcess from 'child_process' -import { Disklet, makeNodeDisklet } from 'disklet' -import path from 'path' - -import { asVersionFile, VersionFile } from './cleaners' - -const versionFileName = 'release-version.json' - -async function main() { - const cwd = path.join(__dirname, '..') - const disklet = makeNodeDisklet(cwd) - - // Determine the current version: - const rawJson = JSON.parse(await disklet.getText(versionFileName)) - const versionFile = asVersionFile(rawJson) - if (versionFile == null) throw new Error('Could not get version file') - - // Update the native project files: - await Promise.all([updateAndroid(disklet, versionFile), updateIos(cwd, versionFile)]) -} - -/** - * Inserts the build information into the Android project files. - */ -async function updateAndroid(disklet: Disklet, versionFile: VersionFile): Promise { - let gradle = await disklet.getText('android/app/build.gradle') - - gradle = gradle.replace(/versionName "[0-9.]+"/g, `versionName "${versionFile.version}"`) - gradle = gradle.replace(/versionCode [0-9]+/g, `versionCode ${versionFile.build}`) - - await disklet.setText('android/app/build.gradle', gradle) -} - -/** - * Inserts the build information into the iOS project files. - */ -function updateIos(cwd: string, versionFile: VersionFile): void { - childProcess.execSync(`agvtool new-marketing-version ${versionFile.version}`, { - cwd: path.join(cwd, 'ios'), - stdio: 'inherit' - }) - childProcess.execSync(`agvtool new-version -all ${versionFile.build}`, { - cwd: path.join(cwd, 'ios'), - stdio: 'inherit' - }) -} - -main().catch(error => { - console.error(error) - throw error -})