-
Notifications
You must be signed in to change notification settings - Fork 141
[Request for comments]: Site Versioning #1870
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 8 commits
4eb63a8
e180361
c229160
e64f91c
778f3cb
95298d4
8eed90c
8453f7a
1ba8c91
f113c22
0769bc7
aca406b
7f559b1
792c156
4fc9090
798d63f
b85641a
9beec61
ad5f0ff
601d343
b52a341
039c0bd
f394060
c77b95e
5f8e427
2362390
3337749
46632f1
5ef1776
850aa18
d6cc776
a4505ce
f8ee5d7
1bdb924
3745f7a
d89e2a6
7d8125d
14fe6ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| {% set title = "Site Versioning" %} | ||
| {% set filename = "versioning" %} | ||
| <span id="title" class="d-none">{{ title }}</span> | ||
|
|
||
| <frontmatter> | ||
| title: "User Guide: {{ title }}" | ||
| layout: userGuide.md | ||
| </frontmatter> | ||
|
|
||
| <span id="link" class="d-none"> | ||
| <md>[_User Guide → {{ title }}_]({{ filename }}.html)</md> | ||
| </span> | ||
|
|
||
| # {{ title }} | ||
|
|
||
| <div class="lead" id="overview"> | ||
|
|
||
| Site versioning is key for documentation use, and websites may want to keep past versions for archival purposes. MarkBind can help you easily archive your site. | ||
| </div> | ||
|
|
||
|
kaixin-hc marked this conversation as resolved.
|
||
| ## Archiving with a CLI command | ||
|
|
||
| Markbind allows you to easily save a version of the site you've built to be hosted at the same site with a modified URL with a single CLI command. All intralinks within the archived site will point to the respective archived pages. By default, the archive folder is called `version`, but you may specify your own directory. | ||
|
kaixin-hc marked this conversation as resolved.
Outdated
|
||
|
|
||
| For example, if your site's base url relative to your domain is 'my_site', and you archive a version named 'v1' in the default archive folder, then by navigating to the url `<domain>/my_site/version/v1/<someFile>` you would have accessed the archived version of someFile. | ||
|
kaixin-hc marked this conversation as resolved.
Outdated
|
||
|
|
||
| A `versions.json` file will be created to track the archived sites you have made, and to exclude the archived sites from being re-archived the next time you make a new version. Modify it with caution, as it may result in unnecessary files being included or necessary files being excluded. | ||
|
kaixin-hc marked this conversation as resolved.
Outdated
|
||
|
|
||
| <include src="cliCommands.md#archiveWarning"> | ||
|
|
||
| {% from "njk/common.njk" import previous_next %} | ||
| {{ previous_next('deployingTheSite', 'markBindInTheProjectWorkflow') }} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -318,16 +318,38 @@ program | |
| .catch(handleError); | ||
| }); | ||
|
|
||
| program | ||
| .command('archive [versionName] [archivePath]') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we put
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea, I agree with this! Will do so unless anyone else disagrees. |
||
| .alias('ar') | ||
| .option('-s, --site-config <file>', 'specify the site config file (default: site.json)') | ||
| .description('archive a version of the site, which is not affected by later changes to the site') | ||
| .action((versionName, userSpecifiedArchivePath, options) => { | ||
| if (!versionName) { | ||
| logger.error('Please specify a name for the archived version.'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| const archivePath = userSpecifiedArchivePath || 'version'; | ||
| const rootFolder = path.resolve(process.cwd()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wonder if we should add in I think minimally we could add in a small note in the cli commands page that the site archived is the |
||
| const outputFolder = path.join(rootFolder, archivePath, versionName); | ||
| new Site(rootFolder, outputFolder, undefined, undefined, options.siteConfig) | ||
| .archive(versionName, archivePath) | ||
| .then(() => { | ||
| logger.info(`Successfully archived ${versionName} at ${archivePath}/${versionName}`); | ||
| }) | ||
| .catch(handleError); | ||
| }); | ||
|
|
||
| program | ||
| .command('deploy') | ||
| .alias('d') | ||
| .description('deploy the site to the repo\'s Github pages') | ||
| .option('-c, --ci [githubTokenName]', 'deploy the site in CI Environment [GITHUB_TOKEN]') | ||
| .option('-s, --site-config <file>', 'specify the site config file (default: site.json)') | ||
| .option('-v, --versions <versionFolder>', 'specify the path where versions are stored') | ||
|
kaixin-hc marked this conversation as resolved.
Outdated
|
||
| .action((options) => { | ||
| const rootFolder = path.resolve(process.cwd()); | ||
| const outputRoot = path.join(rootFolder, '_site'); | ||
| new Site(rootFolder, outputRoot, undefined, undefined, options.siteConfig).deploy(options.ci) | ||
| const site = new Site(rootFolder, outputRoot, undefined, undefined, options.siteConfig); | ||
| site.deploy(options.ci) | ||
|
kaixin-hc marked this conversation as resolved.
Outdated
|
||
| .then(depUrl => (depUrl !== null ? logger.info( | ||
| `The website has been deployed at: ${depUrl}`) | ||
| : logger.info('Deployed!'))) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -12,6 +12,7 @@ module.exports = { | |||||
| PAGE_TEMPLATE_NAME: 'page.njk', | ||||||
| SITE_CONFIG_NAME: 'site.json', | ||||||
| SITE_DATA_NAME: 'siteData.json', | ||||||
| VERSIONS_DATA_NAME: 'versions.json', | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
should we? in view of all the other stuff in there |
||||||
| LAYOUT_SITE_FOLDER_NAME: 'layouts', | ||||||
| LAZY_LOADING_SITE_FILE_NAME: 'LazyLiveReloadLoadingSite.html', | ||||||
| LAZY_LOADING_BUILD_TIME_RECOMMENDATION_LIMIT: 30000, | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -64,6 +64,7 @@ const { | |||||
| PAGE_TEMPLATE_NAME, | ||||||
| SITE_CONFIG_NAME, | ||||||
| SITE_DATA_NAME, | ||||||
| VERSIONS_DATA_NAME, | ||||||
| SITE_FOLDER_NAME, | ||||||
| TEMP_FOLDER_NAME, | ||||||
| TEMPLATE_SITE_ASSET_FOLDER_NAME, | ||||||
|
|
@@ -134,6 +135,12 @@ class Site { | |||||
| this.siteConfig = undefined; | ||||||
| this.siteConfigPath = siteConfigPath; | ||||||
|
|
||||||
| /** | ||||||
| * Archived version information | ||||||
| * @type {undefined | SiteConfig} | ||||||
|
kaixin-hc marked this conversation as resolved.
Outdated
|
||||||
| */ | ||||||
| this.versionData = undefined; | ||||||
|
|
||||||
| // Site wide variable processor | ||||||
| this.variableProcessor = undefined; | ||||||
|
|
||||||
|
|
@@ -652,17 +659,8 @@ class Site { | |||||
|
|
||||||
| try { | ||||||
| await this.readSiteConfig(baseUrl); | ||||||
| this.collectAddressablePages(); | ||||||
| await this.collectBaseUrl(); | ||||||
| this.collectUserDefinedVariablesMap(); | ||||||
| await this.buildAssets(); | ||||||
| await (this.onePagePath ? this.lazyBuildSourceFiles() : this.buildSourceFiles()); | ||||||
| await this.copyCoreWebAsset(); | ||||||
| await this.copyBootstrapTheme(false); | ||||||
| await this.copyFontAwesomeAsset(); | ||||||
| await this.copyOcticonsAsset(); | ||||||
| await this.copyMaterialIconsAsset(); | ||||||
| await this.writeSiteData(); | ||||||
| await this.buildSiteHelper(); | ||||||
|
kaixin-hc marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| this.calculateBuildTimeForGenerate(startTime, lazyWebsiteGenerationString); | ||||||
| if (this.backgroundBuildMode) { | ||||||
| this.backgroundBuildNotViewedFiles(); | ||||||
|
|
@@ -672,6 +670,23 @@ class Site { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Holds the work for generating a site. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
(as opposed to the regeneration ones) |
||||||
| */ | ||||||
| async buildSiteHelper() { | ||||||
| this.collectAddressablePages(); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think missed this part too (implementation is inside this function) |
||||||
| await this.collectBaseUrl(); | ||||||
| this.collectUserDefinedVariablesMap(); | ||||||
| await this.buildAssets(); | ||||||
| await (this.onePagePath ? this.lazyBuildSourceFiles() : this.buildSourceFiles()); | ||||||
| await this.copyCoreWebAsset(); | ||||||
| await this.copyBootstrapTheme(false); | ||||||
| await this.copyFontAwesomeAsset(); | ||||||
| await this.copyOcticonsAsset(); | ||||||
| await this.copyMaterialIconsAsset(); | ||||||
| await this.writeSiteData(); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Helper function for generate(). | ||||||
| */ | ||||||
|
|
@@ -1467,6 +1482,84 @@ class Site { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Archive the current version of the site. | ||||||
|
kaixin-hc marked this conversation as resolved.
Outdated
|
||||||
| * | ||||||
| * @param {string} versionName the name of the version | ||||||
| * @param {string} archivePath the path to the folder to store the archives in | ||||||
| */ | ||||||
| async archive(versionName, archivePath) { | ||||||
| await this.readSiteConfig(); | ||||||
|
|
||||||
| // Save version data | ||||||
| const versionPath = `${archivePath}/${versionName}`; | ||||||
| this.versionData = await this.writeVersionsFile(versionName, versionPath); | ||||||
| // Exclude versioned files from archiving. | ||||||
| const archiveFolders = this.versionData.versions; | ||||||
| archiveFolders.forEach((folder) => { | ||||||
| const filePath = `/${folder.output}/**`; | ||||||
| this.siteConfig.ignore.push(filePath); | ||||||
|
kaixin-hc marked this conversation as resolved.
Outdated
|
||||||
| }); | ||||||
|
|
||||||
| // Used to get accurate intralinks within the archived site: | ||||||
| const archivedBaseUrl = this.siteConfig.baseUrl === '' | ||||||
| ? `/${versionPath}` | ||||||
| : `${this.siteConfig.baseUrl}/${versionPath}`; | ||||||
| this.siteConfig.baseUrl = archivedBaseUrl; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mostly looks like on the right track, I think the biggest missing thing may be:
I realised with this note, perhaps we could also store the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what you mean by "differing baseUrls sholudn't be copied over to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I believe for now, if we do a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I see... It makes sense that differing baseUrls shouldn't be copied over (as they wouldn't be able to be deployed), but I wonder if there are any cases where the baseUrls might differ, but you might still want to include it as part of the same deployment and it would work (something like a root baseUrl being 'root' and the subsite baseUrl being "root/subsite" - it still could be part of the same deployment (?)). I think copying it over is not a significant cost or a bug, and if the user wants to it is easy to manually exclude by indicating that folder as a folder to ignore. But it shouldn't be too hard to exclude the files as well 🤔
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Subsites are meant to be entire sites able to be deployed independently. Our support for subsites is more about content reuse https://markbind.org/userGuide/reusingContents.html#reusing-contents-across-sites, in particular revolving around resolving E.g. Main site, baseUrl is '/cat' subsite index.md If you deploy the subsite independently, If you deploy the main site, This is important wherever there are links involved (link validaiton, relative link resolving,
If I'm getting this part right (ignore as in the e.g. if you have but at the point of archiving,
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The point on
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ang-zeyu Oh :0 So the desired default behaviour, is to serve only the current site, and not any archived sites, because of the cost in copying over large sites? That makes sense. I had assumed the opposite, which is that if the user had archived the site previously they would want to check it when building and serving. Currently, my understanding/implementation is:
And what I understand from your suggestion is that:
I can give it a shot, but do you think I can leave this as an improvement for another PR? I think it might be a little tricky because it affects big commands and features and might take more than a few days to implement carefully (might also make reviewing easier)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, nothing so complex, ignore the additional point on
This is the main issue, it is incorrect to rely on asset copying for this. #1870 (comment) You'll have to implement an independent copying mechanism. If you need convincing, try archiving our documentation with
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. *Please make sure you get the above comment before reading this comment, otherwise it'll only be more confusing
Back to this, my concern here was just with this:
Not in the way you are thinking, but I realised with your comment you'll also need to add the versioned site folders to the This is as:
Back to @jonahtanjz's comment here as well, you'll need to take note of this when implementing the copying as well. (i.e. don't copy archived sites with different baseurl from the current one in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for explaining that again, and for the simple example - that's really useful to test my thoughts. I hadn't realized that we "supported building html files as pages". I had the understanding that "building" a page was converting it from |
||||||
|
|
||||||
| // Create the .tmp folder for storing intermediate results. | ||||||
| fs.emptydirSync(this.tempPath); | ||||||
| // Clean the output folder; create it if not exist. | ||||||
| fs.emptydirSync(this.outputPath); | ||||||
|
|
||||||
| try { | ||||||
| await this.buildSiteHelper(); | ||||||
| } catch (error) { | ||||||
| await Site.rejectHandler(error, [this.tempPath, this.outputPath]); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * If the versions.json file exists, update it with a new version. | ||||||
| * Otherwise, create a new versions file to store information about archived versions | ||||||
| * | ||||||
| * @param {boolean} verbose Flag to emit logs of the operation | ||||||
| * @returns Returns the json object of the current versions data. | ||||||
| */ | ||||||
| async writeVersionsFile(versionName, versionPath, verbose = true) { | ||||||
| const versionsPath = path.join(this.rootPath, VERSIONS_DATA_NAME); | ||||||
| const newVersionData = { | ||||||
| version_name: versionName, | ||||||
| build_ver: MARKBIND_VERSION, | ||||||
|
kaixin-hc marked this conversation as resolved.
Outdated
|
||||||
| output: versionPath, | ||||||
| }; | ||||||
|
|
||||||
| try { | ||||||
| if (!fs.pathExistsSync(versionsPath)) { | ||||||
| // Initialize the versions.json file. | ||||||
| fs.outputJSONSync(versionsPath, { versions: [] }, { spaces: 2 }); | ||||||
| } | ||||||
| const versionsJson = fs.readJSONSync(versionsPath); | ||||||
|
|
||||||
| // Add in or update this new version in the versions file. | ||||||
| const idx = versionsJson.versions.findIndex(vers => vers.output === newVersionData.output); | ||||||
| if (idx === -1) { | ||||||
| versionsJson.versions.push(newVersionData); | ||||||
| } else { | ||||||
| versionsJson.versions[idx] = newVersionData; | ||||||
| } | ||||||
| fs.writeJsonSync(versionsPath, versionsJson, { spaces: 2 }); | ||||||
| if (verbose) { | ||||||
| logger.info('versions.json file updated'); | ||||||
| } | ||||||
|
|
||||||
| return versionsJson; | ||||||
| } catch (error) { | ||||||
| await Site.rejectHandler(error, [this.tempPath, this.outputPath]); | ||||||
| return null; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| deploy(ciTokenVar) { | ||||||
| const defaultDeployConfig = { | ||||||
| branch: 'gh-pages', | ||||||
|
|
||||||

Uh oh!
There was an error while loading. Please reload this page.