-
Notifications
You must be signed in to change notification settings - Fork 67
Expand file tree
/
Copy pathrelease-native-template.mjs
More file actions
201 lines (169 loc) · 6.51 KB
/
release-native-template.mjs
File metadata and controls
201 lines (169 loc) · 6.51 KB
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
import fs from "fs";
import os from "os";
import path from "path";
import { Octokit } from "@octokit/rest";
import { fileURLToPath } from "url";
import simpleGit from "simple-git";
const required = [
"MENDIX_MOBILE_DOCS_PR_GITHUB_PAT",
"NATIVE_TEMPLATE_VERSION_BUMP_TYPE",
];
const missing = required.filter((k) => !process.env[k]);
if (missing.length) {
console.error("Missing env vars:", missing.join(", "));
process.exit(1);
}
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const DOCS_CLONE_PREFIX = "mendix-docs-";
const NATIVE_TEMPLATE_VERSION_BUMP_TYPE =
process.env.NATIVE_TEMPLATE_VERSION_BUMP_TYPE; // patch, minor, major
const MENDIX_MOBILE_DOCS_PR_GITHUB_PAT =
process.env.MENDIX_MOBILE_DOCS_PR_GITHUB_PAT;
const NATIVE_TEMPLATE_VERSION = determineVersionFromBumpType(
NATIVE_TEMPLATE_VERSION_BUMP_TYPE,
);
const GIT_AUTHOR_NAME = "MendixMobile";
const GIT_AUTHOR_EMAIL = "moo@mendix.com";
// Docs Repo Settings
const DOCS_REPO_NAME = "docs";
const DOCS_REPO_OWNER = "MendixMobile";
const DOCS_UPSTREAM_OWNER = "mendix";
const DOCS_BRANCH_NAME = `update-native-template-release-notes-v${NATIVE_TEMPLATE_VERSION}`;
const TARGET_FILE =
"content/en/docs/releasenotes/mobile/native-template/nt-studio-pro-11-parent/nt-17-rn.md";
// Other options:
// - content/en/docs/releasenotes/mobile/native-template/nt-studio-pro-11-parent/nt-15-rn.md
// - content/en/docs/releasenotes/mobile/native-template/nt-studio-pro-11-parent/nt-16-rn.md
const octokit = new Octokit({ auth: MENDIX_MOBILE_DOCS_PR_GITHUB_PAT });
function extractUnreleasedChangelog() {
const changelogPath = path.resolve(
path.join(__dirname, "..", "..", "CHANGELOG.md"),
);
const changelog = fs.readFileSync(changelogPath, "utf-8");
const unreleasedRegex =
/^## \[Unreleased\](.*?)(?=^## \[\d+\.\d+\.\d+\][^\n]*|\Z)/ms;
const match = changelog.match(unreleasedRegex);
if (!match) throw new Error("No [Unreleased] section found!");
const unreleasedContent = match[1].trim();
if (!unreleasedContent) throw new Error("No changes under [Unreleased]!");
return unreleasedContent;
}
// Docs
function injectUnreleasedToDoc(docPath, unreleasedContent) {
const doc = fs.readFileSync(docPath, "utf-8");
const frontmatterMatch = doc.match(/^---[\s\S]*?---/);
if (!frontmatterMatch) throw new Error("Frontmatter not found!");
const frontmatter = frontmatterMatch[0];
const rest = doc.slice(frontmatter.length).trimStart();
const firstReleaseHeadingIndex = rest.search(/^##\s+\d+\.\d+\.\d+/m);
const beforeReleases =
firstReleaseHeadingIndex > 0
? `${rest.slice(0, firstReleaseHeadingIndex).trimEnd()}\n\n`
: "";
const releaseSections =
firstReleaseHeadingIndex >= 0
? rest.slice(firstReleaseHeadingIndex).trimStart()
: rest.trimStart();
const date = new Date();
const formattedDate = date.toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
});
const title = `## ${NATIVE_TEMPLATE_VERSION}\n\n**Release date: ${formattedDate}**`;
return `${frontmatter}\n\n${beforeReleases}${title}\n\n${unreleasedContent}\n\n${releaseSections}`;
}
// This file exists only in the fork (MendixMobile/docs) and not in upstream (mendix/docs).
// Removing it in our branch ensures it doesn't appear in the cross-fork PR diff.
const FORK_SYNC_FILE = ".github/workflows/sync.yml";
async function cloneDocsRepo() {
const git = simpleGit();
const docsCloneDir = fs.mkdtempSync(
path.join(os.tmpdir(), DOCS_CLONE_PREFIX),
);
await git.clone(
`https://x-access-token:${MENDIX_MOBILE_DOCS_PR_GITHUB_PAT}@github.com/${DOCS_REPO_OWNER}/${DOCS_REPO_NAME}.git`,
docsCloneDir,
["--depth", "1"],
);
process.chdir(docsCloneDir);
await git.addConfig("user.name", GIT_AUTHOR_NAME, false, "global");
await git.addConfig("user.email", GIT_AUTHOR_EMAIL, false, "global");
}
async function checkoutLocalBranch(git) {
await git.checkoutLocalBranch(DOCS_BRANCH_NAME);
}
async function updateDocsNTReleaseNotes(unreleasedContent) {
const newDocContent = injectUnreleasedToDoc(TARGET_FILE, unreleasedContent);
fs.writeFileSync(TARGET_FILE, newDocContent, "utf-8");
}
async function createPRUpdateDocsNTReleaseNotes(git) {
// Remove the fork's sync.yml so it doesn't appear in the cross-fork PR diff.
if (fs.existsSync(FORK_SYNC_FILE)) {
await git.rm(FORK_SYNC_FILE);
}
await git.add(TARGET_FILE);
await git.commit(
`docs: update mobile release notes for v${NATIVE_TEMPLATE_VERSION}`,
);
await git.push("origin", DOCS_BRANCH_NAME, ["--force"]);
const prBody = `
Automated sync of the latest release notes for v${NATIVE_TEMPLATE_VERSION} from [native-template](https://github.com/mendix/native-template).
---
**Note:**
This pull request was automatically generated by an automation process managed by the Mobile team.
**Please do not take any action on this pull request unless it has been reviewed and approved by a member of the Mobile team.**
`;
await octokit.pulls.create({
owner: DOCS_UPSTREAM_OWNER,
repo: DOCS_REPO_NAME,
title: `Update mobile app release notes for v${NATIVE_TEMPLATE_VERSION}`,
head: `${DOCS_REPO_OWNER}:${DOCS_BRANCH_NAME}`,
base: "development",
body: prBody,
draft: true,
});
}
// Update NT Release Notes in Docs repo
async function updateNTReleaseNotes(unreleasedContent) {
try {
await cloneDocsRepo();
const git = simpleGit();
await checkoutLocalBranch(git);
updateDocsNTReleaseNotes(unreleasedContent);
await createPRUpdateDocsNTReleaseNotes(git);
} catch (err) {
console.error("❌ Updating NT Release Notes failed:", err);
process.exit(1);
}
}
function determineVersionFromBumpType(bumpType = "patch") {
const packageJsonPath = path.resolve(
path.join(__dirname, "..", "..", "package.json"),
);
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
const currentVersion = packageJson.version;
const [major, minor, patch] = currentVersion.split(".").map(Number);
let newVersion;
switch (bumpType) {
case "major":
newVersion = `${major + 1}.0.0`;
break;
case "minor":
newVersion = `${major}.${minor + 1}.0`;
break;
case "patch":
newVersion = `${major}.${minor}.${patch + 1}`;
break;
default:
throw new Error(
`Invalid bump type for docs release notes: ${bumpType}. Expected one of: patch, minor, major.`,
);
}
return newVersion;
}
(async () => {
const unreleasedContent = extractUnreleasedChangelog();
await updateNTReleaseNotes(unreleasedContent);
})();