mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-17 18:44:45 +02:00
Doc: Auto-publish pre-release info to forum
This commit is contained in:
parent
cfc2a29df6
commit
1fd11588db
@ -928,6 +928,7 @@ packages/tools/git-changelog.test.js
|
|||||||
packages/tools/git-changelog.js
|
packages/tools/git-changelog.js
|
||||||
packages/tools/licenseChecker.js
|
packages/tools/licenseChecker.js
|
||||||
packages/tools/packageJsonLint.js
|
packages/tools/packageJsonLint.js
|
||||||
|
packages/tools/postPreReleasesToForum.js
|
||||||
packages/tools/release-android.js
|
packages/tools/release-android.js
|
||||||
packages/tools/release-cli.js
|
packages/tools/release-cli.js
|
||||||
packages/tools/release-electron.js
|
packages/tools/release-electron.js
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -914,6 +914,7 @@ packages/tools/git-changelog.test.js
|
|||||||
packages/tools/git-changelog.js
|
packages/tools/git-changelog.js
|
||||||
packages/tools/licenseChecker.js
|
packages/tools/licenseChecker.js
|
||||||
packages/tools/packageJsonLint.js
|
packages/tools/packageJsonLint.js
|
||||||
|
packages/tools/postPreReleasesToForum.js
|
||||||
packages/tools/release-android.js
|
packages/tools/release-android.js
|
||||||
packages/tools/release-cli.js
|
packages/tools/release-cli.js
|
||||||
packages/tools/release-electron.js
|
packages/tools/release-electron.js
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"buildPluginDoc": "cd packages/generate-plugin-doc && yarn run buildPluginDoc_",
|
"buildPluginDoc": "cd packages/generate-plugin-doc && yarn run buildPluginDoc_",
|
||||||
"updateMarkdownDoc": "node ./packages/tools/updateMarkdownDoc",
|
"updateMarkdownDoc": "node ./packages/tools/updateMarkdownDoc",
|
||||||
"updateNews": "node ./packages/tools/website/updateNews",
|
"updateNews": "node ./packages/tools/website/updateNews",
|
||||||
|
"postPreReleasesToForum": "node ./packages/tools/postPreReleasesToForum",
|
||||||
"buildSettingJsonSchema": "yarn workspace joplin start settingschema ../../../joplin-website/docs/schema/settings.json",
|
"buildSettingJsonSchema": "yarn workspace joplin start settingschema ../../../joplin-website/docs/schema/settings.json",
|
||||||
"buildTranslations": "node packages/tools/build-translation.js",
|
"buildTranslations": "node packages/tools/build-translation.js",
|
||||||
"buildWebsiteTranslations": "node packages/tools/website/buildTranslations.js",
|
"buildWebsiteTranslations": "node packages/tools/website/buildTranslations.js",
|
||||||
|
99
packages/tools/postPreReleasesToForum.ts
Normal file
99
packages/tools/postPreReleasesToForum.ts
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { pathExists } from 'fs-extra';
|
||||||
|
import { readFile, writeFile } from 'fs/promises';
|
||||||
|
import { gitHubLatestReleases } from './tool-utils';
|
||||||
|
import { config, createPost, createTopic, getForumTopPostByExternalId, getTopicByExternalId, updatePost } from './utils/discourse';
|
||||||
|
import * as compareVersions from 'compare-versions';
|
||||||
|
import dayjs = require('dayjs');
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
processedReleases: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateFilePath = `${__dirname}/postPreReleasesToForum.json`;
|
||||||
|
const betaCategoryId = 10;
|
||||||
|
|
||||||
|
const loadState = async (): Promise<State> => {
|
||||||
|
if (await pathExists(stateFilePath)) {
|
||||||
|
const content = await readFile(stateFilePath, 'utf-8');
|
||||||
|
return JSON.parse(content) as State;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
processedReleases: {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveState = async (state: State) => {
|
||||||
|
await writeFile(stateFilePath, JSON.stringify(state), 'utf-8');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMinorVersion = (fullVersion: string) => {
|
||||||
|
const s = fullVersion.substring(1).split('.');
|
||||||
|
return `${s[0]}.${s[1]}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const main = async () => {
|
||||||
|
const argv = require('yargs').argv;
|
||||||
|
config.key = argv._[0];
|
||||||
|
config.username = argv._[1];
|
||||||
|
|
||||||
|
const state = await loadState();
|
||||||
|
const releases = await gitHubLatestReleases(1, 50);
|
||||||
|
|
||||||
|
releases.sort((a, b) => {
|
||||||
|
return compareVersions(a.tag_name, b.tag_name) <= 0 ? -1 : +1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const startFromVersion = '2.13';
|
||||||
|
|
||||||
|
for (const release of releases) {
|
||||||
|
const minorVersion = getMinorVersion(release.tag_name);
|
||||||
|
|
||||||
|
if (compareVersions(startFromVersion, minorVersion) > 0) continue;
|
||||||
|
|
||||||
|
if (!state.processedReleases[release.tag_name]) {
|
||||||
|
console.info(`Processing release ${release.tag_name}`);
|
||||||
|
|
||||||
|
const externalId = `prerelease-${minorVersion.replace(/\./g, '-')}`;
|
||||||
|
|
||||||
|
const postBody = `## [${release.tag_name}](${release.html_url})\n\n${release.body}`;
|
||||||
|
|
||||||
|
let topic = await getTopicByExternalId(externalId);
|
||||||
|
|
||||||
|
const topicTitle = `Pre-release v${minorVersion} is now available (Updated ${dayjs(new Date()).format('DD/MM/YYYY')})`;
|
||||||
|
|
||||||
|
if (!topic) {
|
||||||
|
console.info('No topic exists - creating one...');
|
||||||
|
|
||||||
|
topic = await createTopic({
|
||||||
|
title: topicTitle,
|
||||||
|
raw: `Download the latest pre-release from here: <https://github.com/laurent22/joplin/releases>\n\n* * *\n\n${postBody}`,
|
||||||
|
category: betaCategoryId,
|
||||||
|
external_id: externalId,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.info('A topic exists - appending the new pre-release to it...');
|
||||||
|
|
||||||
|
const topPost = await getForumTopPostByExternalId(externalId);
|
||||||
|
|
||||||
|
await updatePost(topPost.id, {
|
||||||
|
title: topicTitle,
|
||||||
|
raw: `${topPost.raw}\n\n${postBody}`,
|
||||||
|
edit_reason: 'Auto-updated by script',
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPost(topic.id, {
|
||||||
|
raw: postBody,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
state.processedReleases[release.tag_name] = true;
|
||||||
|
|
||||||
|
await saveState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error('Fatal error', error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
@ -41,7 +41,10 @@ yarn install
|
|||||||
git reset --hard
|
git reset --hard
|
||||||
|
|
||||||
JOPLIN_GITHUB_OAUTH_TOKEN=$JOPLIN_GITHUB_OAUTH_TOKEN yarn run updateMarkdownDoc
|
JOPLIN_GITHUB_OAUTH_TOKEN=$JOPLIN_GITHUB_OAUTH_TOKEN yarn run updateMarkdownDoc
|
||||||
|
|
||||||
|
# Automatically update certain forum posts
|
||||||
yarn run updateNews $DISCOURSE_API_KEY $DISCOURSE_USERNAME
|
yarn run updateNews $DISCOURSE_API_KEY $DISCOURSE_USERNAME
|
||||||
|
yarn run postPreReleasesToForum $DISCOURSE_API_KEY $DISCOURSE_USERNAME
|
||||||
|
|
||||||
# We commit and push the change. It will be a noop if nothing was actually
|
# We commit and push the change. It will be a noop if nothing was actually
|
||||||
# changed
|
# changed
|
||||||
|
@ -18,6 +18,7 @@ export interface GitHubRelease {
|
|||||||
html_url: string;
|
html_url: string;
|
||||||
prerelease: boolean;
|
prerelease: boolean;
|
||||||
draft: boolean;
|
draft: boolean;
|
||||||
|
body: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function insertChangelog(tag: string, changelogPath: string, changelog: string, isPrerelease: boolean, repoTagUrl = '') {
|
async function insertChangelog(tag: string, changelogPath: string, changelog: string, isPrerelease: boolean, repoTagUrl = '') {
|
||||||
@ -375,6 +376,22 @@ export async function gitHubLatestRelease_KeepInCaseMicrosoftBreaksTheApiAgain(r
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const gitHubLatestReleases = async (page: number, perPage: number) => {
|
||||||
|
const response = await fetch(`https://api.github.com/repos/laurent22/joplin/releases?page=${page}&per_page=${perPage}`, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'User-Agent': 'Joplin Forum Updater',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error(`Cannot fetch releases: ${response.statusText}`);
|
||||||
|
|
||||||
|
const releases: GitHubRelease[] = await response.json();
|
||||||
|
if (!releases.length) throw new Error('Cannot find latest release');
|
||||||
|
|
||||||
|
return releases;
|
||||||
|
};
|
||||||
|
|
||||||
export async function githubRelease(project: string, tagName: string, options: any = null): Promise<GitHubRelease> {
|
export async function githubRelease(project: string, tagName: string, options: any = null): Promise<GitHubRelease> {
|
||||||
options = { isDraft: false,
|
options = { isDraft: false,
|
||||||
isPreRelease: false, ...options };
|
isPreRelease: false, ...options };
|
||||||
|
@ -21,6 +21,11 @@ interface ForumTopPost {
|
|||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ForumTopic {
|
||||||
|
id: number;
|
||||||
|
topic_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const config: ApiConfig = {
|
export const config: ApiConfig = {
|
||||||
baseUrl: 'https://discourse.joplinapp.org',
|
baseUrl: 'https://discourse.joplinapp.org',
|
||||||
key: '',
|
key: '',
|
||||||
@ -61,6 +66,7 @@ export const execApi = async (method: HttpMethod, path: string, body: Record<str
|
|||||||
// Ignore - it just means that the error object is a plain string
|
// Ignore - it just means that the error object is a plain string
|
||||||
}
|
}
|
||||||
(error as any).apiObject = apiObject;
|
(error as any).apiObject = apiObject;
|
||||||
|
(error as any).status = response.status;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +83,34 @@ export const getForumTopPostByExternalId = async (externalId: string): Promise<F
|
|||||||
raw: existingForumPost.raw,
|
raw: existingForumPost.raw,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error.status === 404) return null;
|
||||||
if (error.apiObject && error.apiObject.error_type === 'not_found') return null;
|
if (error.apiObject && error.apiObject.error_type === 'not_found') return null;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getTopicByExternalId = async (externalId: string): Promise<ForumTopic> => {
|
||||||
|
try {
|
||||||
|
const existingForumTopic = await execApi(HttpMethod.GET, `t/external_id/${externalId}.json`);
|
||||||
|
return existingForumTopic;
|
||||||
|
} catch (error) {
|
||||||
|
if (error.status === 404) return null;
|
||||||
|
if (error.apiObject && error.apiObject.error_type === 'not_found') return null;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createTopic = async (topic: any): Promise<ForumTopic> => {
|
||||||
|
return execApi(HttpMethod.POST, 'posts', topic);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createPost = async (topicId: number, post: any): Promise<ForumTopic> => {
|
||||||
|
return execApi(HttpMethod.POST, 'posts', {
|
||||||
|
topic_id: topicId,
|
||||||
|
...post,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updatePost = async (postId: number, content: any): Promise<void> => {
|
||||||
|
await execApi(HttpMethod.PUT, `posts/${postId}.json`, content);
|
||||||
|
};
|
||||||
|
@ -8,7 +8,7 @@ import { rootDir } from '../tool-utils';
|
|||||||
import { compileWithFrontMatter, MarkdownAndFrontMatter, stripOffFrontMatter } from './utils/frontMatter';
|
import { compileWithFrontMatter, MarkdownAndFrontMatter, stripOffFrontMatter } from './utils/frontMatter';
|
||||||
import { markdownToHtml } from './utils/render';
|
import { markdownToHtml } from './utils/render';
|
||||||
import { getNewsDate } from './utils/news';
|
import { getNewsDate } from './utils/news';
|
||||||
import { config, execApi, getForumTopPostByExternalId, HttpMethod } from '../utils/discourse';
|
import { config, createTopic, getForumTopPostByExternalId, updatePost } from '../utils/discourse';
|
||||||
const RSS = require('rss');
|
const RSS = require('rss');
|
||||||
|
|
||||||
interface Post {
|
interface Post {
|
||||||
@ -144,7 +144,7 @@ const main = async () => {
|
|||||||
} else {
|
} else {
|
||||||
console.info('Post already exists and has changed: updating it...');
|
console.info('Post already exists and has changed: updating it...');
|
||||||
|
|
||||||
await execApi(HttpMethod.PUT, `posts/${existingForumPost.id}.json`, {
|
await updatePost(existingForumPost.id, {
|
||||||
title: content.title,
|
title: content.title,
|
||||||
raw: content.body,
|
raw: content.body,
|
||||||
edit_reason: 'Auto-updated by script',
|
edit_reason: 'Auto-updated by script',
|
||||||
@ -153,14 +153,14 @@ const main = async () => {
|
|||||||
} else {
|
} else {
|
||||||
console.info('Post does not exists: creating it...');
|
console.info('Post does not exists: creating it...');
|
||||||
|
|
||||||
const response = await execApi(HttpMethod.POST, 'posts', {
|
const topic = await createTopic({
|
||||||
title: content.title,
|
title: content.title,
|
||||||
raw: content.body,
|
raw: content.body,
|
||||||
category: config.newsCategoryId,
|
category: config.newsCategoryId,
|
||||||
external_id: post.id,
|
external_id: post.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const postUrl = `https://discourse.joplinapp.org/t/${response.topic_id}`;
|
const postUrl = `https://discourse.joplinapp.org/t/${topic.topic_id}`;
|
||||||
content.parsed.forum_url = postUrl;
|
content.parsed.forum_url = postUrl;
|
||||||
const compiled = compileWithFrontMatter(content.parsed);
|
const compiled = compileWithFrontMatter(content.parsed);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user