Skip to content

Commit cf92c00

Browse files
committed
fix
1 parent 13b2a62 commit cf92c00

23 files changed

Lines changed: 253 additions & 228 deletions

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,12 @@
1-
# Changelog for code-push-server
1+
# Changelog for code-push-server
2+
3+
## 0.3.0
4+
5+
- 支持灰度发布
6+
- 适配`code-push app add` 命令,应用不在以名字区分平台,而是以类型区分平台
7+
- 数据库表apps新增字段`os`,`platform`
8+
- 完善`code-push release/release-react/release-cordova` 命令
9+
- 数据库表packages新增`is_disabled`,`rollout`字段
10+
- 适配`code-push patch`命令
11+
- 新增`log_report_download`,`log_report_deploy`日志表
12+
- 升级npm依赖包

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ CodePush Server is a CodePush progam server! microsoft CodePush cloud is slow in
1919

2020
## 正确使用code-push热更新
2121

22-
- 苹果允许使用热更新[Apple's developer agreement](https://developer.apple.com/programs/ios/information/iOS_Program_Information_4_3_15.pdf), 但是规定不能弹框提示用户更新,影响用户体验。 而Google Play恰好相反,必须弹框告知用户更新。然而中国的android市场都必须关闭更新弹框,否则会在审核应用时以“请上传最新版本的二进制应用包”驳回应用
22+
- 苹果App允许使用热更新[Apple's developer agreement](https://developer.apple.com/programs/ios/information/iOS_Program_Information_4_3_15.pdf), 为了不影响用户体验,规定必须使用静默更新。 Google Play不能使用静默更新,必须弹框告知用户App有更新。中国的android市场必须采用静默更新(如果弹框提示,App会被“请上传最新版本的二进制应用包”原因驳回)
2323
- react-native 不同平台bundle包不一样,在使用code-push-server的时候必须创建不同的应用来区分(eg. CodePushDemo-ios 和 CodePushDemo-android)
2424
- react-native-code-push只更新资源文件,不会更新java和Objective C,所以npm升级依赖包版本的时候,如果依赖包使用的本地化实现, 这时候必须更改应用版本号(ios修改Info.plist中的CFBundleShortVersionString, android修改build.gradle中的versionName), 然后重新编译app发布到应用商店。
2525
- 推荐使用code-push release-react 命令发布应用,该命令合并了打包和发布命令(eg. code-push release-react CodePushDemo-ios ios -d Production)

app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ if (app.get('env') === 'development') {
111111
res.send(err.message);
112112
log.debug(err);
113113
} else {
114-
res.status(err.status || 500).send(`服务器繁忙,请稍后再试!`);
114+
res.status(err.status || 500).send(err.message);
115115
log.error(err);
116116
}
117117
});

bin/db

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,14 @@ if (command === 'init') {
9393
version_no = _.get(rs,'0.version', '0.0.1');
9494
} catch (e) {
9595
}
96-
if (version_no == '0.2.15') {
96+
if (version_no == '0.3.0') {
9797
console.log('Everything up-to-date.');
9898
process.exit(0);
9999
}
100100
var allSqlFile = [
101-
{version:'0.2.14', 'path':path.resolve(__dirname, '../sql/codepush-v0.2.14.sql')},
102-
{version:'0.2.15', 'path':path.resolve(__dirname, '../sql/codepush-v0.2.15.sql')}
101+
{version:'0.2.14', 'path':path.resolve(__dirname, '../sql/codepush-v0.2.14-patch.sql')},
102+
{version:'0.2.15', 'path':path.resolve(__dirname, '../sql/codepush-v0.2.15-patch.sql')},
103+
{version:'0.3.0', 'path':path.resolve(__dirname, '../sql/codepush-v0.3.0-patch.sql')}
103104
];
104105
for (var i = 0; i < allSqlFile.length; i++) {
105106
if(!_.gt(allSqlFile[i]['version'], version_no)) {

config/config.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ config.development = {
6666
// storageType which is your binary package files store. options value is ("local" | "qiniu" | "s3")
6767
storageType: process.env.STORAGE_TYPE || "local",
6868
// options value is (true | false), when it's true, it will cache updateCheck results in redis.
69-
updateCheckCache: false
69+
updateCheckCache: false,
70+
// options value is (true | false), when it's true, it will cache rollout results in redis
71+
rolloutClientUniqueIdCache: false,
7072
},
7173
// Config for smtp email,register module need validate user email project source https://github.com/nodemailer/nodemailer
7274
smtpConfig:{

config/config.test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ config.test = {
2525
diffNums: 3,
2626
dataDir: os.tmpdir(),
2727
storageType: "local",
28-
updateCheckCache: true
28+
updateCheckCache: true,
29+
rolloutClientUniqueIdCache: false,
2930
},
3031
smtpConfig: false,
3132
redis: {

config/config.testwin.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ config.test = {
2525
diffNums: 3,
2626
dataDir: os.tmpdir(),
2727
storageType: "local",
28-
updateCheckCache: true
28+
updateCheckCache: true,
29+
rolloutClientUniqueIdCache: false,
2930
},
3031
smtpConfig: false,
3132
redis: {

core/const.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,8 @@ define("IS_DISABLED_NO", 0);
3434
define("RELEAS_EMETHOD_PROMOTE", 'Promote');
3535
define("RELEAS_EMETHOD_UPLOAD", 'Upload');
3636

37+
define("DEPLOYMENT_SUCCEEDED", 1);
38+
define("DEPLOYMENT_FAILED", 2);
39+
3740

3841

core/services/client-manager.js

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ var _ = require('lodash');
55
var common = require('../utils/common');
66
var factory = require('../utils/factory');
77
var AppError = require('../app-error');
8+
var config = require('../config');
9+
var log4js = require('log4js');
10+
var log = log4js.getLogger("cps:ClientManager");
811

912
var proto = module.exports = function (){
1013
function ClientManager() {
@@ -15,6 +18,7 @@ var proto = module.exports = function (){
1518
};
1619

1720
const UPDATE_CHECK = "UPDATE_CHECK";
21+
const CHOSEN_MAN = "CHOSEN_MAN";
1822
const EXPIRED = 600;
1923

2024
proto.getUpdateCheckCacheKey = function(deploymentKey, appVersion, label, packageHash) {
@@ -37,9 +41,9 @@ proto.clearUpdateCheckCache = function(deploymentKey, appVersion, label, package
3741
.finally(() => client.quit());
3842
}
3943

40-
proto.updateCheckFromCache = function(deploymentKey, appVersion, label, packageHash) {
44+
proto.updateCheckFromCache = function(deploymentKey, appVersion, label, packageHash, clientUniqueId) {
4145
const self = this;
42-
var updateCheckCache = _.get(require('../config'), 'common.updateCheckCache', false);
46+
var updateCheckCache = _.get(config, 'common.updateCheckCache', false);
4347
if (updateCheckCache === false) {
4448
return self.updateCheck(deploymentKey, appVersion, label, packageHash);
4549
}
@@ -49,14 +53,16 @@ proto.updateCheckFromCache = function(deploymentKey, appVersion, label, packageH
4953
.then((data) => {
5054
if (data) {
5155
try {
56+
log.debug('updateCheckFromCache read from catch');
5257
var obj = JSON.parse(data);
5358
return obj;
5459
} catch (e) {
5560
}
5661
}
57-
return self.updateCheck(deploymentKey, appVersion, label, packageHash)
62+
return self.updateCheck(deploymentKey, appVersion, label, packageHash, clientUniqueId)
5863
.then((rs) => {
5964
try {
65+
log.debug('updateCheckFromCache read from db');
6066
var strRs = JSON.stringify(rs);
6167
client.setexAsync(redisCacheKey, EXPIRED, strRs);
6268
} catch (e) {
@@ -67,7 +73,51 @@ proto.updateCheckFromCache = function(deploymentKey, appVersion, label, packageH
6773
.finally(() => client.quit());
6874
}
6975

70-
proto.updateCheck = function(deploymentKey, appVersion, label, packageHash) {
76+
proto.getChosenManCacheKey = function(deploymentKey, appVersion, clientUniqueId, rollout) {
77+
return [CHOSEN_MAN, deploymentKey, appVersion, clientUniqueId, rollout].join(':');
78+
}
79+
80+
proto.random = function(rollout) {
81+
var r = Math.ceil(Math.random()*10000);
82+
if (r < rollout * 100) {
83+
return Promise.resolve(true);
84+
} else {
85+
return Promise.resolve(false);
86+
}
87+
}
88+
89+
proto.chosenMan = function (rollout, deploymentKey, appVersion, clientUniqueId) {
90+
var self = this;
91+
if (rollout >= 100) {
92+
return Promise.resolve(true);
93+
}
94+
var rolloutClientUniqueIdCache = _.get(config, 'common.rolloutClientUniqueIdCache', false);
95+
if (rolloutClientUniqueIdCache === false) {
96+
return self.random(rollout);
97+
} else {
98+
var client = factory.getRedisClient("default");
99+
var redisCacheKey = self.getChosenManCacheKey(deploymentKey, appVersion, clientUniqueId, rollout);
100+
return client.getAsync(redisCacheKey)
101+
.then((data) => {
102+
if (data == 1) {
103+
return true;
104+
} else if (data == 2) {
105+
return false;
106+
} else {
107+
return self.random(rollout)
108+
.then((r)=>{
109+
return client.setexAsync(redisCacheKey, 60*60*24*7, r ? 1:2)
110+
.then(()=>{
111+
return r;
112+
});
113+
});
114+
}
115+
})
116+
.finally(() => client.quit());
117+
}
118+
}
119+
120+
proto.updateCheck = function(deploymentKey, appVersion, label, packageHash, clientUniqueId) {
71121
var rs = {
72122
downloadURL: "",
73123
downloadUrl: "",
@@ -79,15 +129,17 @@ proto.updateCheck = function(deploymentKey, appVersion, label, packageHash) {
79129
label: "",
80130
packageSize: 0,
81131
updateAppVersion: false,
82-
shouldRunBinaryVersion: false
132+
shouldRunBinaryVersion: false,
133+
rollout: 100
83134
};
135+
var self = this;
84136
if (_.isEmpty(deploymentKey) || _.isEmpty(appVersion)) {
85137
return Promise.reject(new AppError.AppError("please input deploymentKey and appVersion"))
86138
}
87139
return models.Deployments.findOne({where: {deployment_key: deploymentKey}})
88140
.then((dep) => {
89141
if (_.isEmpty(dep)) {
90-
throw new AppError.AppError('does not found deployment');
142+
throw new AppError.AppError('Not found deployment, check deployment key is right.');
91143
}
92144
return models.DeploymentsVersions.findOne({where: {deployment_id: dep.id, app_version: appVersion}});
93145
})
@@ -101,22 +153,20 @@ proto.updateCheck = function(deploymentKey, appVersion, label, packageHash) {
101153
if (packages
102154
&& _.eq(packages.deployment_id, deploymentsVersions.deployment_id)
103155
&& !_.eq(packages.package_hash, packageHash)) {
104-
rs.downloadURL = common.getBlobDownloadUrl(_.get(packages, 'blob_url'));
105-
rs.downloadUrl = common.getBlobDownloadUrl(_.get(packages, 'blob_url'));
156+
rs.downloadUrl = rs.downloadURL = common.getBlobDownloadUrl(_.get(packages, 'blob_url'));
106157
rs.description = _.get(packages, 'description', '');
107-
rs.isAvailable = true;
158+
rs.isAvailable = _.eq(packages.is_disabled, 1) ? false : true;
108159
rs.isMandatory = _.eq(packages.is_mandatory, 1) ? true : false;
109160
rs.appVersion = appVersion;
110161
rs.packageHash = _.get(packages, 'package_hash', '');
111162
rs.label = _.get(packages, 'label', '');
112163
rs.packageSize = _.get(packages, 'size', 0);
113-
rs.shouldRunBinaryVersion = false;
164+
rs.rollout = _.get(packages, 'rollout', 100);
114165
}
115166
return packages;
116167
})
117-
118168
.then((packages) => {
119-
//差异化更新
169+
//增量更新
120170
if (!_.isEmpty(packages) && !_.eq(_.get(packages, 'package_hash', ""), packageHash)) {
121171
return models.PackagesDiff.findOne({where: {package_id:packages.id, diff_against_package_hash: packageHash}})
122172
.then((diffPackage) => {
@@ -159,24 +209,45 @@ proto.getPackagesInfo = function (deploymentKey, label) {
159209
proto.reportStatusDownload = function(deploymentKey, label, clientUniqueId) {
160210
return this.getPackagesInfo(deploymentKey, label)
161211
.then((packages) => {
162-
return models.PackagesMetrics.addOneOnDownloadById(packages.id);
212+
return Promise.all([
213+
models.PackagesMetrics.addOneOnDownloadById(packages.id),
214+
models.LogReportDownload.create({
215+
package_id: packages.id,
216+
client_unique_id: clientUniqueId
217+
})
218+
]);
163219
});
164220
};
165221

166222
proto.reportStatusDeploy = function (deploymentKey, label, clientUniqueId, others) {
167223
return this.getPackagesInfo(deploymentKey, label)
168224
.then((packages) => {
225+
var constConfig = require('../const');
169226
var status = _.get(others, "status");
170227
var packageId = packages.id;
171228
if (_.eq(status, "DeploymentSucceeded")) {
172229
return Promise.all([
230+
models.LogReportDeploy.create({
231+
package_id: packageId,
232+
client_unique_id: clientUniqueId,
233+
previous_label: _.get(others, 'previousLabelOrAppVersion'),
234+
previous_deployment_key: _.get(others, 'previousDeploymentKey'),
235+
status: constConfig.DEPLOYMENT_SUCCEEDED
236+
}),
173237
models.PackagesMetrics.addOneOnInstalledById(packageId),
174238
models.PackagesMetrics.addOneOnActiveById(packageId),
175239
]);
176240
} else if (_.eq(status, "DeploymentFailed")) {
177241
return Promise.all([
242+
models.LogReportDeploy.create({
243+
package_id: packageId,
244+
client_unique_id: clientUniqueId,
245+
previous_label: _.get(others, 'previousLabelOrAppVersion'),
246+
previous_deployment_key: _.get(others, 'previousDeploymentKey'),
247+
status: constConfig.DEPLOYMENT_FAILED
248+
}),
178249
models.PackagesMetrics.addOneOnInstalledById(packageId),
179-
models.PackagesMetrics.addOneOnFailedById(packageId)
250+
models.PackagesMetrics.addOneOnFailedById(packageId),
180251
]);
181252
}else {
182253
return;

core/services/package-manager.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ var common = require('../utils/common');
1212
var os = require('os');
1313
var path = require('path');
1414
var AppError = require('../app-error');
15-
var log4js = require('log4js');
1615
var constConfig = require('../const');
16+
var log4js = require('log4js');
1717
var log = log4js.getLogger("cps:PackageManager");
1818

1919
var proto = module.exports = function (){
@@ -459,15 +459,15 @@ proto.promotePackage = function (sourceDeploymentId, destDeploymentId, params) {
459459
originalLabel: packages.label,
460460
originalDeployment: sourceDeployment.name
461461
};
462-
if (_.isBoolean(isMandatory)) {
463-
create_params.is_mandatory = params.isMandatory ? constConfig.IS_MANDATORY_YES : constConfig.IS_MANDATORY_NO;
462+
if (_.isBoolean(params.isMandatory)) {
463+
create_params.isMandatory = params.isMandatory ? constConfig.IS_MANDATORY_YES : constConfig.IS_MANDATORY_NO;
464464
} else {
465-
create_params.is_mandatory = packages.is_mandatory
465+
create_params.isMandatory = packages.is_mandatory
466466
}
467-
if (_.isBoolean(isDisabled)) {
468-
create_params.is_disabled = params.isDisabled ? constConfig.IS_DISABLED_YES : constConfig.IS_DISABLED_NO;
467+
if (_.isBoolean(params.isDisabled)) {
468+
create_params.isMandatory = params.isDisabled ? constConfig.IS_DISABLED_YES : constConfig.IS_DISABLED_NO;
469469
} else {
470-
create_params.is_disabled = packages.is_disabled
470+
create_params.isMandatory = packages.is_disabled
471471
}
472472
return self.createPackage(destDeploymentId, deploymentsVersions.app_version, packages.package_hash, packages.manifest_blob_url, packages.blob_url, create_params);
473473
});
@@ -509,8 +509,8 @@ proto.rollbackPackage = function (deploymentVersionId, targetLabel, rollbackUid)
509509
releaseMethod: 'Rollback',
510510
releaseUid: rollbackUid,
511511
isMandatory: rollbackPackage.is_mandatory,
512-
isDisabled: packages.is_disabled,
513-
rollout: packages.rollout,
512+
isDisabled: rollbackPackage.is_disabled,
513+
rollout: rollbackPackage.rollout,
514514
size: rollbackPackage.size,
515515
description: rollbackPackage.description,
516516
originalLabel: rollbackPackage.label,

0 commit comments

Comments
 (0)