-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathpromote-beta-experimental-traffic.ts
101 lines (88 loc) · 3.32 KB
/
promote-beta-experimental-traffic.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
* Promotes a Beta/Experimental traffic release.
*/
import yargs from 'yargs/yargs';
import {
createVersionsUpdatePullRequest,
ensureForwardPromote,
octokit,
runPromoteJob,
} from './promote-job';
interface ExperimentConfig {
// ExperimentConfig has other fields, but here we only care about the following:
expiration_date_utc?: string;
define_experiment_constant?: string;
}
interface ExperimentsConfig {
experimentA: ExperimentConfig;
experimentB: ExperimentConfig;
experimentC: ExperimentConfig;
}
const jobName = 'promote-beta-experimental-traffic.ts';
const {amp_version: AMP_VERSION} = yargs(process.argv.slice(2))
.options({amp_version: {type: 'string', default: ''}})
.parseSync();
async function fetchActiveExperiments(
ampVersion: string
): Promise<ExperimentsConfig> {
const response = await octokit.repos.getContent({
owner: 'ampproject',
repo: 'amphtml',
ref: ampVersion,
path: 'build-system/global-configs/experiments-config.json',
});
if (!('content' in response.data)) {
throw new Error(
'Unexpected response when fetching experiments-config.json from ampproject/amphtml'
);
}
return JSON.parse(
Buffer.from(response.data.content, 'base64').toString()
) as ExperimentsConfig;
}
function maybeRtv(experiment: ExperimentConfig, rtv: string): string | null {
const {define_experiment_constant, expiration_date_utc} = experiment;
if (!define_experiment_constant || !expiration_date_utc) {
return null;
}
// Regardless of when this job was run, experiments should still be available
// if the base (pre any cherry-picks) AMP version was generated from a commit
// on or before that experiment's expiration date. This is to prevent a
// situation where an experiment suddenly gets turned off by a cherry-pick
// that affects the traffic channels.
const rtvDateStr = rtv.slice(2, 8);
const expirationDateStr = expiration_date_utc.slice(2).replace(/-/g, '');
if (expirationDateStr < rtvDateStr) {
return null;
}
return rtv;
}
void runPromoteJob(jobName, async () => {
return createVersionsUpdatePullRequest(async (currentVersions) => {
// We assume that the AMP version number is the same for beta-opt-in and experimental-opt-in, and only differ in their RTV prefix.
const ampVersion = AMP_VERSION || currentVersions['beta-opt-in'].slice(2);
// for scheduled promotions, check that the new version is a forward promote
if (!AMP_VERSION) {
ensureForwardPromote(ampVersion, [
currentVersions['beta-traffic'],
currentVersions.stable,
currentVersions.lts,
]);
}
const activeExperiments = await fetchActiveExperiments(ampVersion);
return {
ampVersion,
versionsChanges: {
'beta-traffic': `03${ampVersion}`,
'experimental-traffic': `00${ampVersion}`,
experimentA: maybeRtv(activeExperiments.experimentA, `10${ampVersion}`),
experimentB: maybeRtv(activeExperiments.experimentB, `11${ampVersion}`),
experimentC: maybeRtv(activeExperiments.experimentC, `12${ampVersion}`),
},
title: `⏫ Promoting release ${ampVersion} to Beta/Experimental traffic channel`,
body: `Promoting release ${ampVersion} from Beta/Experimental opt-in to traffic`,
branch: `beta-experimental-traffic-${ampVersion}`,
qaRequire: true,
};
});
});