Skip to content

Commit

Permalink
Merge branch 'main' into migration
Browse files Browse the repository at this point in the history
  • Loading branch information
tianzhou committed Mar 14, 2024
2 parents 483aa91 + 2136742 commit b3cb5c7
Show file tree
Hide file tree
Showing 2 changed files with 270 additions and 204 deletions.
231 changes: 132 additions & 99 deletions .github/actions/upsert-issue/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,105 +114,12 @@ function run() {
// Otherwise, create a new issue.
if (issue) {
if (issue.plan) {
const components = issue.plan.split("/");
const planUid = components[components.length - 1];
const planRes = yield fetch(`${url}/v1/projects/${projectId}/plans/${planUid}`, {
method: "GET",
headers,
});
const planData = yield planRes.json();
if (planData.message) {
throw new Error(planData.message);
}
core.info("Check existing plan for update:\n" + JSON.stringify(planData, null, 2));
// Currently, Bytebase only allows to in-place update existing spec in the plan steps. And it
// doesn't allow to add new spec or remove spec. Attempt to add/remove will encounter errors:
//
// {"code":3, "message":"cannot add specs to plan", "details":[]}
// {"code":3, "message":"cannot remove specs from plan", "details":[]}
//
// Return error if we attempt to add new migration file to the existing issue.
for (const change of changes) {
let matchedSpec;
for (const step of planData.steps) {
for (const spec of step.specs) {
if (change.id == spec.id) {
matchedSpec = spec;
break;
}
}
if (!matchedSpec) {
throw new Error('Bytebase disallow adding new migration file to the existing issue: ' + change.file);
}
}
}
// Return error if we attempt to remove migration file from the existing issue.
for (const step of planData.steps) {
for (const spec of step.specs) {
let matchedSpec;
for (const change of changes) {
if (change.database == spec.changeDatabaseConfig.target && change.id == spec.id) {
matchedSpec = spec;
break;
}
}
if (!matchedSpec) {
throw new Error('Bytebase disallow removing migration file from the existing issue.');
}
}
}
let updatePlan = false;
for (const step of planData.steps) {
for (const spec of step.specs) {
for (const change of changes) {
if (change.database == spec.changeDatabaseConfig.target && change.id == spec.id) {
const components = spec.changeDatabaseConfig.sheet.split("/");
const sheetUid = components[components.length - 1];
// Fetch the full content
const queryParams = new URLSearchParams({ "raw": "true" });
const sheetRes = yield fetch(`${url}/v1/projects/${projectId}/sheets/${sheetUid}?${queryParams}`, {
method: "GET",
headers,
});
const sheetData = yield sheetRes.json();
if (sheetData.message) {
throw new Error(sheetData.message);
}
// If there is a change to the existing migration file, then we create a new sheet and
// update the plan with the new sheet
const oldContent = Buffer.from(sheetData.content, 'base64').toString();
if (change.content != oldContent) {
core.info("Migration file has changed " + change.file);
core.info((0, diff_1.createPatch)('difference', oldContent, change.content));
const createdSheetData = yield createSheet(change, title);
spec.changeDatabaseConfig.sheet = createdSheetData.name;
updatePlan = true;
}
break;
}
}
}
}
if (updatePlan) {
const queryParams = new URLSearchParams({ "update_mask": "steps" });
const planRes = yield fetch(`${url}/v1/projects/${projectId}/plans/${planUid}?${queryParams}`, {
method: "PATCH",
headers,
body: JSON.stringify({ steps: planData.steps }),
});
const newPlanData = yield planRes.json();
if (newPlanData.message) {
throw new Error(newPlanData.message);
}
core.info("Updated plan:\n" + JSON.stringify(newPlanData, null, 2));
const issueURL = `${url}/projects/${projectId}/issues/${issue.uid}`;
core.info("Successfully updated issue at " + issueURL);
}
else {
const issueURL = `${url}/projects/${projectId}/issues/${issue.uid}`;
core.info("Skip plan update. No migration file changed since the last time.");
core.info("View issue at " + issueURL);
}
updateIssuePlan(issue, changes, title);
}
else {
// In theory, every issue must have a plan, otherwise issue creation will return error:
// {"code":3, "message":"plan is required", "details":[]}
throw new Error('Missing plan from the existing issue.');
}
}
else {
Expand Down Expand Up @@ -387,6 +294,132 @@ function createIssue(planName, assignee, title, description) {
return {};
});
}
function updateIssuePlan(issue, changes, title) {
return __awaiter(this, void 0, void 0, function* () {
const components = issue.plan.split("/");
const planUid = components[components.length - 1];
const planRes = yield fetch(`${projectUrl}/plans/${planUid}`, {
method: "GET",
headers,
});
const planData = yield planRes.json();
if (planData.message) {
throw new Error(planData.message);
}
core.info("Check existing plan for update:\n" + JSON.stringify(planData, null, 2));
// Currently, Bytebase only allows to in-place update existing spec in the plan steps. And it
// doesn't allow to add new spec or remove spec. Attempt to add/remove will encounter errors:
//
// {"code":3, "message":"cannot add specs to plan", "details":[]}
// {"code":3, "message":"cannot remove specs from plan", "details":[]}
//
// Return error if we attempt to add new migration file to the existing issue.
for (const change of changes) {
let matchedSpec;
for (const step of planData.steps) {
for (const spec of step.specs) {
if (change.id == spec.id) {
matchedSpec = spec;
break;
}
}
if (!matchedSpec) {
throw new Error('Bytebase disallow adding new migration file to the existing issue: ' + change.file);
}
}
}
// Return error if we attempt to remove migration file from the existing issue.
for (const step of planData.steps) {
for (const spec of step.specs) {
let matchedSpec;
for (const change of changes) {
if (change.database == spec.changeDatabaseConfig.target && change.id == spec.id) {
matchedSpec = spec;
break;
}
}
if (!matchedSpec) {
throw new Error('Bytebase disallow removing migration file from the existing issue.');
}
}
}
// Return error if we attempt to update a rollout task NOT in the following states
const allowedStates = ["NOT_STARTED", "CANCELED", "FAILED"];
const rolloutUid = components[components.length - 1];
const rolloutRes = yield fetch(`${projectUrl}/rollouts/${rolloutUid}`, {
method: "GET",
headers,
});
const rolloutData = yield rolloutRes.json();
if (rolloutData.message) {
throw new Error(rolloutData.message);
}
core.info("Check existing rollout for update:\n" + JSON.stringify(rolloutData, null, 2));
for (const stage of rolloutData.stages) {
for (const task of stage.tasks) {
for (const change of changes) {
if (change.id == task.specId) {
if (!allowedStates.includes(task.status)) {
throw new Error('Can not update migration file. Expect task status ' + task.status + ' not in ' + allowedStates.toString());
}
}
}
}
}
let updatePlan = false;
for (const step of planData.steps) {
for (const spec of step.specs) {
for (const change of changes) {
if (change.database == spec.changeDatabaseConfig.target && change.id == spec.id) {
const components = spec.changeDatabaseConfig.sheet.split("/");
const sheetUid = components[components.length - 1];
// Fetch the full content
const queryParams = new URLSearchParams({ "raw": "true" });
const sheetRes = yield fetch(`${projectUrl}/sheets/${sheetUid}?${queryParams}`, {
method: "GET",
headers,
});
const sheetData = yield sheetRes.json();
if (sheetData.message) {
throw new Error(sheetData.message);
}
// If there is a change to the existing migration file, then we create a new sheet and
// update the plan with the new sheet
const oldContent = Buffer.from(sheetData.content, 'base64').toString();
if (change.content != oldContent) {
core.info("Migration file has changed " + change.file);
core.info((0, diff_1.createPatch)('difference', oldContent, change.content));
const createdSheetData = yield createSheet(change, title);
spec.changeDatabaseConfig.sheet = createdSheetData.name;
updatePlan = true;
}
break;
}
}
}
}
if (updatePlan) {
const queryParams = new URLSearchParams({ "update_mask": "steps" });
const planRes = yield fetch(`${projectUrl}/plans/${planUid}?${queryParams}`, {
method: "PATCH",
headers,
body: JSON.stringify({ steps: planData.steps }),
});
const newPlanData = yield planRes.json();
if (newPlanData.message) {
throw new Error(newPlanData.message);
}
core.info("Updated plan:\n" + JSON.stringify(newPlanData, null, 2));
const issueURL = `${projectUrl}/issues/${issue.uid}`;
core.info("Successfully updated issue at " + issueURL);
}
else {
const issueURL = `${projectUrl}/issues/${issue.uid}`;
core.info("Skip plan update. No migration file changed since the last time.");
core.info("View issue at " + issueURL);
}
});
}
function createRollout(planName) {
return __awaiter(this, void 0, void 0, function* () {
try {
Expand Down
Loading

0 comments on commit b3cb5c7

Please sign in to comment.