Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cli/commands/releaseCommand/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Options = {
skipBundle: boolean;
skipCleanup: boolean;
outputBundleDir: string;
hashCalc?: boolean;
}

program.command('release')
Expand All @@ -36,6 +37,7 @@ program.command('release')
.option('--enable <bool>', 'make the release to be enabled', parseBoolean, true)
.option('--rollout <number>', 'rollout percentage (0-100)', parseFloat)
.option('--skip-bundle <bool>', 'skip bundle process', parseBoolean, false)
.option('--hash-calc <bool>', 'calculates the bundle file hash used for packageHash in the release history (Requires setting --skip-bundle to true)', parseBoolean)
.option('--skip-cleanup <bool>', 'skip cleanup process', parseBoolean, false)
.option('--output-bundle-dir <string>', 'name of directory containing the bundle file created by the "bundle" command', OUTPUT_BUNDLE_DIR)
.action(async (options: Options) => {
Expand All @@ -46,6 +48,11 @@ program.command('release')
process.exit(1);
}

if (options.hashCalc && !options.skipBundle) {
console.error('--hash-calc option can be used only when --skip-bundle is set to true.');
process.exit(1);
}

await release(
config.bundleUploader,
config.getReleaseHistory,
Expand All @@ -64,6 +71,7 @@ program.command('release')
options.skipBundle,
options.skipCleanup,
`${options.outputPath}/${options.outputBundleDir}`,
options.hashCalc,
)

console.log('🚀 Release completed.')
Expand Down
32 changes: 31 additions & 1 deletion cli/commands/releaseCommand/release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import path from "path";
import { bundleCodePush } from "../bundleCommand/bundleCodePush.js";
import { addToReleaseHistory } from "./addToReleaseHistory.js";
import type { CliConfigInterface } from "../../../typings/react-native-code-push.d.ts";
import { generatePackageHashFromDirectory } from "../../utils/hash-utils.js";
import { unzip } from "../../utils/unzip.js";

export async function release(
bundleUploader: CliConfigInterface['bundleUploader'],
Expand All @@ -22,12 +24,21 @@ export async function release(
skipBundle: boolean,
skipCleanup: boolean,
bundleDirectory: string,
hashCalc?: boolean,
): Promise<void> {
const bundleFileName = skipBundle
? readBundleFileNameFrom(bundleDirectory)
: await bundleCodePush(framework, platform, outputPath, entryFile, jsBundleName, bundleDirectory);
const bundleFilePath = `${bundleDirectory}/${bundleFileName}`;

const packageHash = await (() => {
if (skipBundle && hashCalc) {
return calcHashFromBundleFile(bundleFilePath);
}
// If not using --skip-bundle, the bundleFileName represents package hash already.
return bundleFileName;
})();

const downloadUrl = await (async () => {
try {
const { downloadUrl } = await bundleUploader(bundleFilePath, platform, identifier);
Expand All @@ -42,7 +53,7 @@ export async function release(
appVersion,
binaryVersion,
downloadUrl,
bundleFileName,
packageHash,
getReleaseHistory,
setReleaseHistory,
platform,
Expand Down Expand Up @@ -70,3 +81,22 @@ function readBundleFileNameFrom(bundleDirectory: string): string {
const bundleFilePath = path.join(bundleDirectory, files[0]);
return path.basename(bundleFilePath);
}

async function calcHashFromBundleFile(bundleFilePath: string): Promise<string> {
const tempDir = path.resolve(path.join(path.dirname(bundleFilePath), 'temp_contents_for_hash_calc'));
const zipFilePath = path.resolve(bundleFilePath);

if (fs.existsSync(tempDir)) {
fs.rmSync(tempDir, { recursive: true, force: true });
}
fs.mkdirSync(tempDir, { recursive: true });

try {
await unzip(zipFilePath, tempDir);
const hash = await generatePackageHashFromDirectory(tempDir, tempDir);
console.log(`log: Calculated package hash from existing bundle file: ${hash}`);
return hash;
} finally {
fs.rmSync(tempDir, { recursive: true, force: true });
}
}
6 changes: 4 additions & 2 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"commander": "^12.1.0",
"shelljs": "^0.10.0",
"xcode": "^3.0.1",
"yazl": "^3.3.1"
"yazl": "^3.3.1",
"yauzl": "^3.2.0"
},
"peerDependencies": {
"ts-node": ">=10"
Expand All @@ -28,6 +29,7 @@
"node": ">=18"
},
"devDependencies": {
"@types/yazl": "^3.3.0"
"@types/yazl": "^3.3.0",
"@types/yauzl": "^2.10.3"
}
}
46 changes: 46 additions & 0 deletions cli/utils/unzip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import fs from "fs";
import path from "path";
import yauzl from "yauzl";

export function unzip(zipPath: string, outputDir: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
yauzl.open(zipPath, { lazyEntries: true }, (err, zipFile) => {
if (err) return reject(err);

zipFile.readEntry();

zipFile.on("entry", (entry) => {
const fullPath = path.join(outputDir, entry.fileName);

// Handle directory entry
if (/\/$/.test(entry.fileName)) {
fs.mkdir(fullPath, { recursive: true }, (err) => {
if (err) return reject(err);
zipFile.readEntry();
});
return;
}

// Handle file entry
zipFile.openReadStream(entry, (err, readStream) => {
if (err) return reject(err);

fs.mkdir(path.dirname(fullPath), { recursive: true }, (err) => {
if (err) return reject(err);

const writeStream = fs.createWriteStream(fullPath);
readStream.pipe(writeStream);

// Continue to the next entry after writing
writeStream.on("close", () => {
zipFile.readEntry();
});
});
});
});

zipFile.on("end", resolve);
zipFile.on("error", reject);
});
});
}
42 changes: 42 additions & 0 deletions package-lock.json

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

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@
"semver": "^7.3.5",
"shelljs": "^0.10.0",
"xcode": "^3.0.1",
"yazl": "^3.3.1"
"yazl": "^3.3.1",
"yauzl": "^3.2.0"
},
"peerDependencies": {
"expo": ">=50.0.0",
Expand All @@ -104,6 +105,7 @@
"@types/q": "^1.5.4",
"@types/semver": "^7.5.8",
"@types/shelljs": "^0.8.15",
"@types/yauzl": "^2.10.3",
"archiver": "latest",
"babel-jest": "^29.7.0",
"body-parser": "latest",
Expand Down