mirror of
https://github.com/EthanShoeDev/fressh.git
synced 2026-01-11 06:12:51 +00:00
180 lines
5.5 KiB
TypeScript
180 lines
5.5 KiB
TypeScript
/**
|
|
* https://docs.expo.dev/guides/local-app-production/
|
|
*/
|
|
import * as fsp from 'fs/promises';
|
|
import * as path from 'path';
|
|
import { boolean, command, flag, oneOf, option, run } from 'cmd-ts';
|
|
import { z } from 'zod';
|
|
import packageJson from '../package.json' with { type: 'json' };
|
|
import { cmd } from './script-lib';
|
|
|
|
async function getSecrets(): Promise<{
|
|
keystoreBase64: string;
|
|
keystoreAlias: string;
|
|
keystorePassword: string;
|
|
}> {
|
|
const { stdout: rawBwItemString } = await cmd(
|
|
`bw get item "fressh keystore" --raw`,
|
|
{
|
|
stdio: 'pipe',
|
|
},
|
|
);
|
|
const bwItemSchema = z.looseObject({
|
|
login: z.looseObject({
|
|
username: z.string(),
|
|
password: z.string(),
|
|
}),
|
|
fields: z.array(
|
|
z.looseObject({
|
|
name: z.string(),
|
|
value: z.string(),
|
|
}),
|
|
),
|
|
});
|
|
const bwItem = bwItemSchema.parse(JSON.parse(rawBwItemString) as unknown, {
|
|
reportInput: true,
|
|
});
|
|
const keystoreBase64 = bwItem.fields.find(
|
|
(field) => field.name === 'keystore',
|
|
)?.value;
|
|
if (!keystoreBase64) throw new Error('Keystore not found');
|
|
return {
|
|
keystoreBase64,
|
|
keystoreAlias: bwItem.login.username,
|
|
keystorePassword: bwItem.login.password,
|
|
};
|
|
}
|
|
|
|
const signedBuildCommand = command({
|
|
name: 'signed-build',
|
|
description: 'Build a signed release build of the app',
|
|
args: {
|
|
format: option({
|
|
long: 'format',
|
|
type: oneOf(['aab', 'apk']),
|
|
short: 'f',
|
|
description: 'The format of the build to build',
|
|
defaultValue: () => 'aab',
|
|
}),
|
|
ghRelease: flag({
|
|
long: 'gh-release',
|
|
type: boolean,
|
|
short: 'g',
|
|
description:
|
|
'Whether to create a GitHub release (deprecated, use release-it instead)',
|
|
defaultValue: () => false,
|
|
}),
|
|
},
|
|
handler: async ({ format, ghRelease }) => {
|
|
{
|
|
if (ghRelease && format !== 'apk')
|
|
throw new Error('ghRelease is only supported for apk builds');
|
|
|
|
console.log(
|
|
'Making signed build. Format:',
|
|
format,
|
|
'GH Release:',
|
|
ghRelease,
|
|
);
|
|
const secrets = await getSecrets();
|
|
await cmd(`pnpm run prebuild:clean`);
|
|
|
|
// Ensure keystore is in the right place
|
|
// https://docs.expo.dev/guides/local-app-production/#create-an-upload-key
|
|
// Generated with:
|
|
// sudo keytool -genkey -v -keystore fressh-upload-key.keystore -alias fressh-key-alias -keyalg RSA -keysize 2048 -validity 10000
|
|
const keystorePath = `./android/app/fressh-upload-key.keystore`;
|
|
const keystoreFileName = path.basename(keystorePath);
|
|
// const bufferShouldEqual = await fsp.readFile(keystoreFileName, 'base64');
|
|
// await fsp.writeFile(
|
|
// './debug.log',
|
|
// JSON.stringify(
|
|
// {
|
|
// ...secrets,
|
|
// bufferShouldEqual,
|
|
// },
|
|
// null,
|
|
// 2,
|
|
// ),
|
|
// );
|
|
await fsp.writeFile(
|
|
keystorePath,
|
|
Buffer.from(secrets.keystoreBase64, 'base64'),
|
|
'base64',
|
|
);
|
|
console.log(`Keystore written to ${keystorePath}`);
|
|
|
|
// Ensure gradle.properties is configured
|
|
// https://docs.expo.dev/guides/local-app-production/#update-gradle-variables
|
|
const gradlePropertiesSuffix = `
|
|
FRESSH_UPLOAD_STORE_FILE=${keystoreFileName}
|
|
FRESSH_UPLOAD_KEY_ALIAS=${secrets.keystoreAlias}
|
|
FRESSH_UPLOAD_STORE_PASSWORD=${secrets.keystorePassword}
|
|
FRESSH_UPLOAD_KEY_PASSWORD=${secrets.keystorePassword}
|
|
`;
|
|
const currentGradleProperties = await fsp.readFile(
|
|
'./android/gradle.properties',
|
|
'utf8',
|
|
);
|
|
|
|
if (!currentGradleProperties.includes(gradlePropertiesSuffix.trim())) {
|
|
await fsp.writeFile(
|
|
'./android/gradle.properties',
|
|
`${currentGradleProperties}\n\n${gradlePropertiesSuffix}`,
|
|
);
|
|
console.log(`Gradle properties written to ./android/gradle.properties`);
|
|
}
|
|
|
|
// Ensure there is a release signing config in android/app/build.gradle
|
|
// https://docs.expo.dev/guides/local-app-production/#add-signing-config-to-buildgradle
|
|
const releaseSigningConfig = `
|
|
release {
|
|
if (project.hasProperty('FRESSH_UPLOAD_STORE_FILE')) {
|
|
storeFile file(FRESSH_UPLOAD_STORE_FILE)
|
|
storePassword FRESSH_UPLOAD_STORE_PASSWORD
|
|
keyAlias FRESSH_UPLOAD_KEY_ALIAS
|
|
keyPassword FRESSH_UPLOAD_KEY_PASSWORD
|
|
}
|
|
}`;
|
|
const currentBuildGradle = await fsp.readFile(
|
|
'./android/app/build.gradle',
|
|
'utf8',
|
|
);
|
|
if (!currentBuildGradle.includes(releaseSigningConfig.trim())) {
|
|
const newBuildGradle = currentBuildGradle
|
|
.replace(
|
|
/signingConfigs \{([\s\S]*?)\}/, // Modify existing signingConfigs without removing debug
|
|
(match) => {
|
|
if (match.includes('release {')) {
|
|
return match.replace(
|
|
/release \{([\s\S]*?)\}/,
|
|
releaseSigningConfig,
|
|
);
|
|
}
|
|
return match.trim() + releaseSigningConfig;
|
|
},
|
|
)
|
|
.replace(
|
|
/buildTypes \{([\s\S]*?)release \{([\s\S]*?)signingConfig signingConfigs\.debug/, // Ensure release config uses signingConfigs.release
|
|
`buildTypes { $1release { $2signingConfig signingConfigs.release`,
|
|
);
|
|
await fsp.writeFile('./android/app/build.gradle', newBuildGradle);
|
|
console.log(`Build gradle written to ./android/app/build.gradle`);
|
|
}
|
|
|
|
const bundleCommand =
|
|
format === 'aab' ? 'bundleRelease' : 'assembleRelease';
|
|
await cmd(`./gradlew app:${bundleCommand}`, {
|
|
relativeCwd: './android',
|
|
});
|
|
|
|
if (ghRelease)
|
|
await cmd(
|
|
`gh release create v${packageJson.version} ./android/app/build/outputs/apk/release/app-release.apk`,
|
|
);
|
|
}
|
|
},
|
|
});
|
|
|
|
void run(signedBuildCommand, process.argv.slice(2));
|