Skip to content
Open
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ RUN yes | bubblewrap doctor

WORKDIR /app

ENTRYPOINT ["bubblewrap"]

12 changes: 6 additions & 6 deletions packages/cli/src/lib/cmds/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ interface SigningKeyPasswords {
keyPassword: string;
}

class Build {
export class Build {
constructor(
private args: ParsedArgs,
private androidSdkTools: AndroidSdkTools,
Expand Down Expand Up @@ -111,9 +111,9 @@ class Build {
async signApk(signingKey: SigningKeyInfo, passwords: SigningKeyPasswords): Promise<void> {
await this.androidSdkTools.apksigner(
signingKey.path,
`"${passwords.keystorePassword}"`,
passwords.keystorePassword,
signingKey.alias,
`"${passwords.keyPassword}"`,
passwords.keyPassword,
APK_ALIGNED_FILE_NAME, // input file path
APK_SIGNED_FILE_NAME,
);
Expand All @@ -126,8 +126,8 @@ class Build {
async signAppBundle(signingKey: SigningKeyInfo, passwords: SigningKeyPasswords): Promise<void> {
await this.jarSigner.sign(
signingKey,
`"${passwords.keystorePassword}"`,
`"${passwords.keyPassword}"`,
passwords.keystorePassword,
passwords.keyPassword,
APP_BUNDLE_BUILD_OUTPUT_FILE_NAME,
APP_BUNDLE_SIGNED_FILE_NAME);
}
Expand Down Expand Up @@ -189,7 +189,7 @@ class Build {
passwords = await this.getPasswords(signingKey);
signingKey = {
...signingKey,
...{path: `"${signingKey.path}"`}, // Wrap path in quotes in case there are spaces
...{path: signingKey.path},
...(this.args.signingKeyPath ? {path: this.args.signingKeyPath} : null),
...(this.args.signingKeyAlias ? {alias: this.args.signingKeyAlias} : null),
};
Expand Down
68 changes: 68 additions & 0 deletions packages/cli/src/spec/lib/cmds/BuildSpec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import mock from 'mock-fs';
import {build as buildCmd, Build} from '../../../lib/cmds/build';
import * as core from '@bubblewrap/core';
import {computeChecksum} from '../../../lib/cmds/shared';
import {MockPrompt} from '../../mock/MockPrompt';

describe('build', () => {
describe('#build', () => {
it('passes unquoted passwords to apksigner and jarsigner', async () => {
// Create a minimal twa-manifest.json
const manifest = JSON.stringify({
packageId: 'com.example.twa',
host: 'example.com',
name: 'Test',
display: 'standalone',
themeColor: '#FFFFFF',
navigationColor: '#000000',
backgroundColor: '#FFFFFF',
startUrl: '/',
signingKey: {path: './android.keystore', alias: 'android'},
});
const checksum = computeChecksum(Buffer.from(manifest));

mock({
'twa-manifest.json': manifest,
'manifest-checksum.txt': checksum,
});

const mockAndroidSdkTools: any = {
checkBuildTools: async () => true,
installBuildTools: async () => {},
zipalignOnlyVerification: async (_: string) => {},
apksigner: async (_ks: any, ksPass: string, _alias: any, keyPass: string, _in: any, _out: any) => {
expect(ksPass).toEqual('mystorepass');
expect(keyPass).toEqual('mykeypass');
},
};

// Instead of running the full build flow, instantiate Build directly and test signApk
const mockGradleWrapper: any = {
assembleRelease: async () => {},
bundleRelease: async () => {},
};
const mockKeyTool: any = {};
const mockJarSigner: any = {
sign: async (_signingKey: any, storepass: string, keypass: string) => {
expect(storepass).toEqual('mystorepass');
expect(keypass).toEqual('mykeypass');
},
};

// Set env so we bypass interactive prompts
process.env['BUBBLEWRAP_KEYSTORE_PASSWORD'] = 'mystorepass';
process.env['BUBBLEWRAP_KEY_PASSWORD'] = 'mykeypass';

const args = { manifest: 'twa-manifest.json', directory: '.', skipSigning: false } as any;
const buildInstance = new Build(args, mockAndroidSdkTools, mockKeyTool, mockGradleWrapper,
mockJarSigner, undefined, new MockPrompt() as any);

await buildInstance.signApk({path: './android.keystore', alias: 'android'} as any,
{keystorePassword: 'mystorepass', keyPassword: 'mykeypass'});
await buildInstance.signAppBundle({path: './android.keystore', alias: 'android'} as any,
{keystorePassword: 'mystorepass', keyPassword: 'mykeypass'});

mock.restore();
});
});
});