-
Notifications
You must be signed in to change notification settings - Fork 155
feat(CodeSigningPlugin): auto-embed public key into native project files #1381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7545f75
dda6146
a6948b5
be8801c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@callstack/repack": minor | ||
| --- | ||
|
|
||
| Add `publicKeyPath` and `nativeProjectPaths` options to `CodeSigningPlugin`. When `publicKeyPath` is set, the plugin automatically embeds the public key into `Info.plist` (iOS) and `strings.xml` (Android) during compilation, removing the need for manual native file setup. The `embedPublicKey` utility is also exported for standalone use. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,17 @@ import type { Compiler as RspackCompiler } from '@rspack/core'; | |
| import jwt from 'jsonwebtoken'; | ||
| import type { Compiler as WebpackCompiler } from 'webpack'; | ||
| import { type CodeSigningPluginConfig, validateConfig } from './config.js'; | ||
| import { embedPublicKey } from './embedPublicKey.js'; | ||
|
|
||
| function resolveProjectPath( | ||
| projectRoot: string, | ||
| configPath?: string | ||
| ): string | undefined { | ||
| if (!configPath) return undefined; | ||
| return path.isAbsolute(configPath) | ||
| ? configPath | ||
| : path.resolve(projectRoot, configPath); | ||
| } | ||
|
|
||
| export class CodeSigningPlugin { | ||
| private chunkFilenames: Set<string>; | ||
|
|
@@ -26,7 +37,6 @@ export class CodeSigningPlugin { | |
| mainOutputFilename: string, | ||
| excludedChunks: string[] | RegExp[] | ||
| ): boolean { | ||
| /** Exclude non-chunks & main chunk as it's always local */ | ||
| if (!this.chunkFilenames.has(file) || file === mainOutputFilename) { | ||
| return false; | ||
| } | ||
|
|
@@ -39,6 +49,72 @@ export class CodeSigningPlugin { | |
| }); | ||
| } | ||
|
|
||
| private embedPublicKeyInNativeProjects(compiler: RspackCompiler) { | ||
| if (!this.config.publicKeyPath) { | ||
| return; | ||
| } | ||
|
|
||
| const logger = compiler.getInfrastructureLogger('RepackCodeSigningPlugin'); | ||
| const projectRoot = compiler.context; | ||
|
|
||
| const publicKeyPath = resolveProjectPath( | ||
| projectRoot, | ||
| this.config.publicKeyPath | ||
| )!; | ||
|
|
||
| if (!fs.existsSync(publicKeyPath)) { | ||
| logger.warn( | ||
| `Public key not found at ${publicKeyPath}. ` + | ||
| 'Skipping automatic embedding into native project files.' | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| const result = embedPublicKey({ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we would want to think of a way to not always try to embed, like some kind of cache or something if we know it was already embedded? Maybe not worth the effort though, what do you think? |
||
| publicKeyPath, | ||
| projectRoot, | ||
| iosInfoPlistPath: resolveProjectPath( | ||
| projectRoot, | ||
| this.config.nativeProjectPaths?.ios | ||
| ), | ||
| androidStringsXmlPath: resolveProjectPath( | ||
| projectRoot, | ||
| this.config.nativeProjectPaths?.android | ||
| ), | ||
| }); | ||
|
|
||
| if (result.error) { | ||
| logger.warn(result.error); | ||
| return; | ||
| } | ||
|
|
||
| if (result.ios.modified) { | ||
| logger.info(`Embedded public key in iOS Info.plist: ${result.ios.path}`); | ||
| } else if (result.ios.error) { | ||
| logger.warn(`Failed to embed public key in iOS: ${result.ios.error}`); | ||
| } else { | ||
| logger.warn( | ||
| 'Could not find iOS Info.plist. Skipping auto-embedding for iOS. ' + | ||
| 'Use nativeProjectPaths.ios or manually add the public key to Info.plist.' | ||
| ); | ||
| } | ||
|
|
||
| if (result.android.modified) { | ||
| logger.info( | ||
| `Embedded public key in Android strings.xml: ${result.android.path}` | ||
| ); | ||
| } else if (result.android.error) { | ||
| logger.warn( | ||
| `Failed to embed public key in Android: ${result.android.error}` | ||
| ); | ||
| } else { | ||
| logger.warn( | ||
| 'Could not find Android strings.xml. Skipping auto-embedding for Android. ' + | ||
| 'Use nativeProjectPaths.android or manually add the public key to strings.xml.' | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| apply(compiler: RspackCompiler): void; | ||
| apply(compiler: WebpackCompiler): void; | ||
|
|
||
|
|
@@ -62,15 +138,27 @@ export class CodeSigningPlugin { | |
| */ | ||
| const TOKEN_BUFFER_SIZE = 1280; | ||
| /** | ||
| * Used to denote beginning of the code-signing section of the bundle | ||
| * Used to denote the beginning of the code-signing section of the bundle | ||
| * alias for "Repack Code-Signing Signature Begin" | ||
| */ | ||
| const BEGIN_CS_MARK = '/* RCSSB */'; | ||
|
|
||
| const privateKeyPath = path.isAbsolute(this.config.privateKeyPath) | ||
| ? this.config.privateKeyPath | ||
| : path.resolve(compiler.context, this.config.privateKeyPath); | ||
| const privateKey = fs.readFileSync(privateKeyPath); | ||
| const privateKeyPath = resolveProjectPath( | ||
| compiler.context, | ||
| this.config.privateKeyPath | ||
| )!; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same thing about the |
||
|
|
||
| let privateKey: Buffer; | ||
| try { | ||
| privateKey = fs.readFileSync(privateKeyPath); | ||
| } catch (err: unknown) { | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| throw new Error( | ||
| `Failed to read private key from ${privateKeyPath}: ${message}` | ||
| ); | ||
| } | ||
|
|
||
| this.embedPublicKeyInNativeProjects(compiler); | ||
|
|
||
| const excludedChunks = Array.isArray(this.config.excludeChunks) | ||
| ? this.config.excludeChunks | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this
!seems like it would be better to avoid, perhaps we can just handle the undefined case