Skip to content

Commit

Permalink
feat: add upgrade process
Browse files Browse the repository at this point in the history
  • Loading branch information
agoose77 committed Jun 19, 2024
1 parent a2a6e37 commit 58894c2
Show file tree
Hide file tree
Showing 13 changed files with 682 additions and 36 deletions.
1 change: 1 addition & 0 deletions packages/myst-cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './build/index.js';
export * from './cli/index.js';
export * from './config.js';
export * from './init/index.js';
export * from './frontmatter.js';
export * from './plugins.js';
export * from './process/index.js';
Expand Down
21 changes: 2 additions & 19 deletions packages/myst-cli/src/init/gh-actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import path from 'node:path';
import inquirer from 'inquirer';
import chalk from 'chalk';
import type { ISession } from 'myst-cli-utils';
import { makeExecutable, writeFileToFolder } from 'myst-cli-utils';
import { writeFileToFolder } from 'myst-cli-utils';
import { getGithubUrl } from '../../utils/github.js';
import { checkFolderIsGit, checkAtGitRoot } from '../../utils/git.js';

function createGithubPagesAction({
defaultBranch = 'main',
Expand Down Expand Up @@ -97,24 +98,6 @@ jobs:
`;
}

async function checkFolderIsGit(): Promise<boolean> {
try {
await makeExecutable('git status', null)();
return true;
} catch (error) {
return false;
}
}

async function checkAtGitRoot(): Promise<boolean> {
try {
fs.readdirSync('.git');
return true;
} catch (error) {
return false;
}
}

async function prelimGitChecks(session: ISession): Promise<string | undefined> {
const inGitRepo = await checkFolderIsGit();
if (!inGitRepo) {
Expand Down
1 change: 1 addition & 0 deletions packages/myst-cli/src/init/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './init.js';
48 changes: 31 additions & 17 deletions packages/myst-cli/src/init/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import type { ISession } from '../session/types.js';
import { startServer } from '../build/site/start.js';
import { githubCurvenoteAction, githubPagesAction } from './gh-actions/index.js';
import { getGithubUrl } from '../utils/github.js';
import { upgradeJupyterBook } from './jupyter-book/upgrade.js';
import { fsExists } from '../utils/fsExists.js';

const VERSION_CONFIG = '# See docs at: https://mystmd.org/guide/frontmatter\nversion: 1\n';

Expand Down Expand Up @@ -54,6 +56,7 @@ Learn more about this CLI and MyST Markdown at: ${chalk.bold('https://mystmd.org
`;


export async function init(session: ISession, opts: InitOptions) {
const { project, site, writeTOC, ghPages, ghCurvenote } = opts;

Expand Down Expand Up @@ -93,24 +96,35 @@ export async function init(session: ISession, opts: InitOptions) {
await writeConfigs(session, '.', { siteConfig, projectConfig });
}
} else {
// If no config is present, write it explicitly to include comments.
const configFile = defaultConfigFile(session, '.');
let configData: string;
let configDoc: string;
if (site && !project) {
configData = `${VERSION_CONFIG}${SITE_CONFIG}`;
configDoc = 'site';
} else if (project && !site) {
configData = `${VERSION_CONFIG}${createProjectConfig({ github })}`;
configDoc = 'project';
} else {
configData = `${VERSION_CONFIG}${createProjectConfig({ github })}${SITE_CONFIG}`;
configDoc = 'project and site';
// Is this a Jupyter Book?
if (await fsExists('_config.yml')) {
const configFile = defaultConfigFile(session, '.');
session.log.info(
`📘 Found a legacy Jupyter Book, writing new config file: ${chalk.blue(path.resolve(configFile))}`,
);
await upgradeJupyterBook(session, configFile);
}
// Otherwise, write some default configs
else {
// If no config is present, write it explicitly to include comments.
const configFile = defaultConfigFile(session, '.');
let configData: string;
let configDoc: string;
if (site && !project) {
configData = `${VERSION_CONFIG}${SITE_CONFIG}`;
configDoc = 'site';
} else if (project && !site) {
configData = `${VERSION_CONFIG}${createProjectConfig({ github })}`;
configDoc = 'project';
} else {
configData = `${VERSION_CONFIG}${createProjectConfig({ github })}${SITE_CONFIG}`;
configDoc = 'project and site';
}
session.log.info(
`💾 Writing new ${configDoc} config file: ${chalk.blue(path.resolve(configFile))}`,
);
fs.writeFileSync(configFile, configData);
}
session.log.info(
`💾 Writing new ${configDoc} config file: ${chalk.blue(path.resolve(configFile))}`,
);
fs.writeFileSync(configFile, configData);
}
if (writeTOC) {
await loadConfig(session, '.');
Expand Down
180 changes: 180 additions & 0 deletions packages/myst-cli/src/init/jupyter-book/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { z } from 'zod';
import { defined } from '../../utils/defined.js';
import type { Config, ProjectConfig, SiteConfig } from 'myst-config';

const JupyterBookConfig = z.object({
title: z.string().optional(),
author: z.string().optional(),
copyright: z.string().optional(),
logo: z.string().optional(),
exclude_patterns: z.array(z.string()).optional(),
parse: z
.object({
myst_enable_extensions: z.union([z.null(), z.array(z.string())]).optional(),
myst_url_schemes: z.union([z.null(), z.array(z.string())]).optional(),
myst_dmath_double_inline: z.boolean().default(true),
})
.optional(),
execute: z
.object({
eval_regex: z.string().default('^.*$'),
raise_on_error: z.boolean().default(false),
show_tb: z.boolean().default(false),
execute_notebooks: z
.union([
z.literal('auto'),
z.literal('cache'),
z.literal('force'),
z.literal('inline'),
z.literal('off'),
z.literal(false),
])
.default('auto'),
cache: z.string().optional(),
timeout: z.number().gte(-1).default(30),
allow_errors: z.boolean().default(false),
stderr_output: z
.enum(['show', 'remove', 'remove-warn', 'warn', 'error', 'severe'])
.default('show'),
run_in_temp: z.boolean().default(false),
exclude_patterns: z.array(z.string()).optional(),
})
.optional(),
html: z
.object({
favicon: z.string().optional(),
use_edit_page_button: z.boolean().optional(),
use_repository_button: z.boolean().optional(),
use_issues_button: z.boolean().optional(),
extra_footer: z.string().optional(),
analytics: z
.object({
plausible_analytics_domain: z.string().optional(),
google_analytics_id: z.string().optional(),
})
.optional(),
home_page_in_navbar: z.boolean().optional(),
baseurl: z.string().optional(),
comments: z
.object({
hypothesis: z.union([z.boolean(), z.record(z.any())]).optional(),
utterances: z.union([z.boolean(), z.record(z.any())]).optional(),
})
.optional(),
announcement: z.string().optional(),
})
.optional(),
latex: z.object({ latex_engine: z.string().default('pdflatex') }).optional(),
launch_buttons: z
.object({
notebook_interface: z.string().optional(),
binderhub_url: z.string().optional(),
jupyterhub_url: z.string().optional(),
thebe: z.boolean().optional(),
colab_url: z.string().optional(),
})
.optional(),
repository: z
.object({
url: z.string().optional(),
path_to_book: z.string().optional(),
branch: z.string().optional(),
})
.optional(),
sphinx: z
.object({
extra_extensions: z.union([z.null(), z.array(z.string())]).optional(),
local_extensions: z.union([z.null(), z.record(z.any())]).optional(),
recursive_update: z.boolean().optional(),
config: z.union([z.null(), z.record(z.any())]).optional(),
})
.optional(),
});

export type JupyterBookConfig = z.infer<typeof JupyterBookConfig>;
export function validateJupyterBookConfig(config: unknown): JupyterBookConfig | undefined {
const result = JupyterBookConfig.safeParse(config);
if (!result.success) {
console.error(result.error);
return undefined;
} else {
return result.data;
}
}

function parseGitHubRepoURL(url: string): string | undefined {
//eslint-disable-next-line
const match = url.match(/(?:git@|https:\/\/)github.com[:\/](.*)(?:.git)?/);
if (!match) {
return undefined;
}
return match[1];
}

export function upgradeConfig(data: JupyterBookConfig): Pick<Config, 'project' | 'site'> {
const project: ProjectConfig = {};
const siteOptions: SiteConfig['options'] = {};
const site: SiteConfig = {
options: siteOptions,
template: "book-theme"
};

if (defined(data.title)) {
project.title = data.title;
}

if (defined(data.author)) {
const authors = data.author.split(/,\s*(?:and\s)?\s*|\s+and\s+/);
if (authors.length === 1) {
project.authors = [{ name: data.author }]; // TODO prompt user for alias?
} else {
project.authors = authors.map((name) => ({ name }));
}
}

if (defined(data.copyright)) {
project.copyright = data.copyright;
}

if (defined(data.logo)) {
siteOptions.logo = data.logo;
}

if (defined(data.exclude_patterns)) {
project.exclude = data.exclude_patterns;
}

if (defined(data.html?.favicon)) {
siteOptions.favicon = data.html.favicon;
}

if (defined(data.html?.analytics?.google_analytics_id)) {
siteOptions.analytics_google = data.html.analytics.google_analytics_id;
}

if (defined(data.html?.analytics?.plausible_analytics_domain)) {
siteOptions.analytics_plausible = data.html.analytics.plausible_analytics_domain;
}

const repo = defined(data.repository?.url) ? parseGitHubRepoURL(data.repository?.url) : undefined;
if (defined(repo)) {
project.github = repo;
}

// Do we want to enable thebe and mybinder?
if (
defined(repo) &&
(defined(data.launch_buttons?.binderhub_url) || !!data.launch_buttons?.thebe)
) {
project.thebe = {
binder: {
repo: repo,
provider: 'github',
url: data.launch_buttons?.binderhub_url,
ref: data.repository?.branch,
},
};
}

return { project, site };
}
1 change: 1 addition & 0 deletions packages/myst-cli/src/init/jupyter-book/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './upgrade.js';

0 comments on commit 58894c2

Please sign in to comment.