Skip to content

Commit 21855ae

Browse files
authored
Merge pull request mozilla#340 from kumar303/sign-without-id
feat: `web-ext sign` now supports extensions without explicit IDs
2 parents 4ae37fe + fdf3d3b commit 21855ae

File tree

4 files changed

+333
-55
lines changed

4 files changed

+333
-55
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"babel-preset-es2015": "6.9.0",
7171
"babel-preset-stage-2": "6.11.0",
7272
"chai": "3.5.0",
73+
"copy-dir": "0.3.0",
7374
"coveralls": "2.11.9",
7475
"deepcopy": "0.6.3",
7576
"eslint": "3.0.1",

src/cmd/sign.js

+94-29
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
/* @flow */
2+
import path from 'path';
3+
import fs from 'mz/fs';
24
import {signAddon as defaultAddonSigner} from '../util/es6-modules';
35

46
import defaultBuilder from './build';
5-
import {InvalidManifest} from '../errors';
67
import {withTempDir} from '../util/temp-dir';
7-
import getValidatedManifest from '../util/manifest';
8+
import {onlyErrorsWithCode, WebExtError} from '../errors';
9+
import getValidatedManifest, {getManifestId} from '../util/manifest';
810
import {prepareArtifactsDir} from '../util/artifacts';
911
import {createLogger} from '../util/logger';
1012

1113
const log = createLogger(__filename);
12-
14+
export const extensionIdFile = '.web-extension-id';
1315

1416
export default function sign(
1517
{verbose, sourceDir, artifactsDir, apiKey, apiSecret,
16-
apiUrlPrefix, timeout}: Object,
18+
apiUrlPrefix, id, timeout}: Object,
1719
{build=defaultBuilder, signAddon=defaultAddonSigner,
1820
preValidatedManifest=null}: Object = {}): Promise {
1921

@@ -28,39 +30,102 @@ export default function sign(
2830
}
2931
})
3032
.then((manifestData) => {
31-
if (!manifestData.applications) {
32-
// TODO: remove this when signing supports manifests
33-
// without IDs: https://github.com/mozilla/web-ext/issues/178
34-
throw new InvalidManifest(
35-
'applications.gecko.id in manifest.json is required for signing');
33+
return Promise.all([
34+
build({sourceDir, artifactsDir: tmpDir.path()}, {manifestData}),
35+
getIdFromSourceDir(sourceDir),
36+
])
37+
.then(([buildResult, idFromSourceDir]) => {
38+
return {buildResult, manifestData, idFromSourceDir};
39+
});
40+
})
41+
.then(({buildResult, manifestData, idFromSourceDir}) => {
42+
const manifestId = getManifestId(manifestData);
43+
if (id && manifestId) {
44+
throw new WebExtError(
45+
`Cannot set custom ID ${id} because manifest.json ` +
46+
`declares ID ${manifestId}`);
47+
}
48+
if (manifestId) {
49+
id = manifestId;
50+
}
51+
if (!id && idFromSourceDir) {
52+
log.info(
53+
'Using previously auto-generated extension ID: ' +
54+
`${idFromSourceDir}`);
55+
id = idFromSourceDir;
56+
}
57+
if (!id) {
58+
log.warn('No extension ID specified (it will be auto-generated)');
3659
}
37-
return manifestData;
60+
return signAddon({
61+
apiKey,
62+
apiSecret,
63+
apiUrlPrefix,
64+
timeout,
65+
verbose,
66+
id,
67+
xpiPath: buildResult.extensionPath,
68+
version: manifestData.version,
69+
downloadDir: artifactsDir,
70+
});
3871
})
39-
.then((manifestData) => {
40-
return build(
41-
{sourceDir, artifactsDir: tmpDir.path()},
42-
{manifestData})
43-
.then((buildResult) => {
44-
return {buildResult, manifestData};
45-
});
72+
.then((signingResult) => {
73+
if (signingResult.id) {
74+
return saveIdToSourceDir(sourceDir, signingResult.id)
75+
.then(() => signingResult);
76+
} else {
77+
return signingResult;
78+
}
4679
})
47-
.then(({buildResult, manifestData}) => signAddon({
48-
apiKey,
49-
apiSecret,
50-
apiUrlPrefix,
51-
timeout,
52-
verbose,
53-
xpiPath: buildResult.extensionPath,
54-
id: manifestData.applications.gecko.id,
55-
version: manifestData.version,
56-
downloadDir: artifactsDir,
57-
}))
5880
.then((signingResult) => {
5981
// All information about the downloaded files would have
6082
// already been logged by signAddon().
61-
log.info(signingResult.success ? 'SUCCESS' : 'FAIL');
83+
if (signingResult.success) {
84+
log.info(`Extension ID: ${signingResult.id}`);
85+
log.info('SUCCESS');
86+
} else {
87+
log.info('FAIL');
88+
}
6289
return signingResult;
6390
});
6491
}
6592
);
6693
}
94+
95+
96+
export function getIdFromSourceDir(sourceDir: string): Promise {
97+
const filePath = path.join(sourceDir, extensionIdFile);
98+
return fs.readFile(filePath)
99+
.then((content) => {
100+
let lines = content.toString().split('\n');
101+
lines = lines.filter((line) => {
102+
line = line.trim();
103+
if (line && !line.startsWith('#')) {
104+
return line;
105+
}
106+
});
107+
let id = lines[0];
108+
log.debug(`Found extension ID ${id} in ${filePath}`);
109+
if (!id) {
110+
throw new WebExtError(`No ID found in extension ID file ${filePath}`);
111+
}
112+
return id;
113+
})
114+
.catch(onlyErrorsWithCode('ENOENT', () => {
115+
log.debug(`No ID file found at: ${filePath}`);
116+
}));
117+
}
118+
119+
120+
export function saveIdToSourceDir(sourceDir: string, id: string): Promise {
121+
const filePath = path.join(sourceDir, extensionIdFile);
122+
return fs.writeFile(filePath,
123+
[
124+
'# This file was created by https://github.com/mozilla/web-ext',
125+
'# Your auto-generated extension ID for addons.mozilla.org is:',
126+
id.toString(),
127+
].join('\n'))
128+
.then(() => {
129+
log.debug(`Saved auto-generated ID ${id} to ${filePath}`);
130+
});
131+
}

src/program.js

+7
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,13 @@ Example: $0 --help run.
203203
demand: true,
204204
type: 'string',
205205
},
206+
'id': {
207+
describe:
208+
'A custom ID for the extension. This will override ' +
209+
'any ID declared by manifest.json.',
210+
demand: false,
211+
type: 'string',
212+
},
206213
'timeout' : {
207214
describe: 'Number of milliseconds to wait before giving up',
208215
type: 'number',

0 commit comments

Comments
 (0)