From 1a9338d00b0ad9146de1ebdf3acf0e382832ddac Mon Sep 17 00:00:00 2001 From: Ze Bateira Date: Fri, 16 Apr 2021 18:16:40 +0100 Subject: [PATCH] feat: exclude scheduled posts and links feat: automatically deploy scheduled posts or links --- .github/workflows/scheduled-publishing.yml | 26 ++++++ package-lock.json | 2 + package.json | 2 + scripts/scheduled-publishing.js | 98 ++++++++++++++++++++++ src/.vuepress/plugins/pageData.js | 44 +++++++++- src/.vuepress/theme/layouts/Blog.vue | 10 +-- src/.vuepress/theme/layouts/BlogPost.vue | 13 ++- src/.vuepress/theme/util/blogUtils.js | 9 +- src/_blog/events.md | 2 - src/_blog/welcome-to-ipfs-weekly-131.md | 1 - 10 files changed, 189 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/scheduled-publishing.yml create mode 100755 scripts/scheduled-publishing.js diff --git a/.github/workflows/scheduled-publishing.yml b/.github/workflows/scheduled-publishing.yml new file mode 100644 index 00000000..317fea66 --- /dev/null +++ b/.github/workflows/scheduled-publishing.yml @@ -0,0 +1,26 @@ +name: Check for scheduled posts to publish +on: + schedule: + - cron: '* 9,21 * * *' +jobs: + check-n-publish: + name: Check for scheduled posts and publish if any found + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Install dependencies + run: npm ci + - name: Check for scheduled posts + run: echo "::set-output name=should_publish::$(${{ github.workspace }}/scripts/scheduled-publishing.js)" + id: should_publish_step + - name: Trigger Fleek Build + if: ${{ steps.should_publish_step.outputs.should_publish == 'true' }} + id: deploy + uses: fleekhq/action-deploy@v1 + with: + apiKey: ${{ secrets.FLEEK_API_KEY }} + - name: Scheduled posts published + if: ${{ steps.should_publish_step.outputs.should_publish == 'true' }} + run: echo "Check https://blog.ipfs.io" + diff --git a/package-lock.json b/package-lock.json index f2ee17d3..1194306d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "eslint-plugin-prettier": "^3.3.1", "eslint-plugin-promise": "^5.1.0", "eslint-plugin-vue": "^7.9.0", + "gray-matter": "^4.0.2", "husky": "^6.0.0", "lint-staged": "^10.5.4", "lodash": "^4.17.21", @@ -41,6 +42,7 @@ "markdown-it-task-lists": "^2.1.1", "markdown-it-video": "^0.6.3", "npm-run-all": "^4.1.5", + "p-map": "^4.0.0", "postcss": "^7.0.35", "prettier": "^2.2.1", "slug": "^4.0.3", diff --git a/package.json b/package.json index 38bedadc..33e1421c 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "eslint-plugin-prettier": "^3.3.1", "eslint-plugin-promise": "^5.1.0", "eslint-plugin-vue": "^7.9.0", + "gray-matter": "^4.0.2", "husky": "^6.0.0", "lint-staged": "^10.5.4", "lodash": "^4.17.21", @@ -38,6 +39,7 @@ "markdown-it-task-lists": "^2.1.1", "markdown-it-video": "^0.6.3", "npm-run-all": "^4.1.5", + "p-map": "^4.0.0", "postcss": "^7.0.35", "prettier": "^2.2.1", "slug": "^4.0.3", diff --git a/scripts/scheduled-publishing.js b/scripts/scheduled-publishing.js new file mode 100755 index 00000000..3cf9f6b8 --- /dev/null +++ b/scripts/scheduled-publishing.js @@ -0,0 +1,98 @@ +#!/usr/bin/env node + +'use strict' + +/** + * Scheduled Publishing of posts and links. + * This script checks for any blog posts and links that are scheduled to + * be published, but are to be published today. If there is at least + * one found, a build is trigged in fleek, updating the website + * with the latest data. + */ + +const fs = require('fs') +const path = require('path') + +const pMap = require('p-map') +const matter = require('gray-matter') +const dayjs = require('dayjs') + +const isBetween = require('dayjs/plugin/isBetween') +dayjs.extend(isBetween) + +async function main() { + // read files contents concurrently, 10 at a time + const contentDir = path.resolve(__dirname, '../src/_blog') + const files = await pMap( + await fs.promises.readdir(contentDir), + (filename) => { + if (!filename.endsWith('.md')) { + return null + } + + return fs.promises.readFile(path.resolve(contentDir, filename), 'utf8') + }, + { concurrency: 10 } + ) + + const output = files + // exclude possible nulls + .filter((result) => !!result) + // process files into publishable items (posts or links) + .reduce((items, fileContent) => { + const metadata = matter(fileContent).data + + if (metadata.type && metadata.data) { + // links + items.push(...metadata.data) + } else { + items.push(metadata) + } + + return items + }, []) + // check if there is at least one publish date in the future + .some((item) => { + let publishDate + + // blog post + if (item.permalink) { + publishDate = item.date + } + + // links + if (item.url && item.publish_date) { + publishDate = item.publish_date + } + + if (!publishDate) { + return false + } + + // we trim to the hour to avoid counting the initial delays on triggering + // the job, which could cause the time calculations to exclude specific + // scheduled dates and times. + const now = dayjs(new Date()).startOf('hour') + const timeMatch = dayjs(new Date(publishDate)).isBetween( + now.subtract(12, 'hour'), + now + ) + + // using stderr since stdout is used for GH workflow output + if (timeMatch) { + console.error(`Found post/link to publish: "${item.title}"`) + } + + return timeMatch + }) + + if (!output) { + console.error('Nothing found to publish.') + } + + // output true or false: should trigger publish or not + // GH Workflow step reads from stdout + console.log(output) +} + +main() diff --git a/src/.vuepress/plugins/pageData.js b/src/.vuepress/plugins/pageData.js index e503fa2a..79d5e59f 100644 --- a/src/.vuepress/plugins/pageData.js +++ b/src/.vuepress/plugins/pageData.js @@ -1,4 +1,32 @@ const slug = require('slug') +const dayjs = require('dayjs') + +function shouldBeHidden(frontmatter) { + const isDateInFuture = (date) => dayjs(new Date(date)).isAfter(dayjs()) + let shouldHide = frontmatter.hidden || false + + // sitemap config + if (frontmatter.sitemap) { + shouldHide = shouldHide || frontmatter.sitemap.exclude + } + + // scheduled posts + // see auto-publishing of scheduled posts here: https://github.com/ipfs/ipfs-blog/issues/147 + if ( + !shouldHide && + frontmatter.permalink && // permalink is unique to posts + frontmatter.date + ) { + shouldHide = shouldHide || isDateInFuture(frontmatter.date) + } + + // scheduled links (path is unique to links) + if (!shouldHide && frontmatter.path && frontmatter.publish_date) { + shouldHide = shouldHide || isDateInFuture(frontmatter.publish_date) + } + + return shouldHide +} module.exports = (options, context) => ({ extendPageData($page) { @@ -18,8 +46,8 @@ module.exports = (options, context) => ({ frontmatter.authorKey = authorKey } - // exclude a page from the feed & robots if excluded from sitemap - if (frontmatter.sitemap && frontmatter.sitemap.exclude) { + // exclude hidden pages (sitemap config, future publishes, etc) + if (shouldBeHidden(frontmatter)) { frontmatter.feed = { enable: false, } @@ -30,6 +58,18 @@ module.exports = (options, context) => ({ } else { frontmatter.meta = [noIndex] } + + frontmatter.hidden = true + $page.hidden = true + } + + // set links has hidden (future publishes, etc) + if (frontmatter.data) { + frontmatter.data.forEach((item) => { + if (shouldBeHidden(item)) { + item.hidden = true + } + }) } }, }) diff --git a/src/.vuepress/theme/layouts/Blog.vue b/src/.vuepress/theme/layouts/Blog.vue index 64eec2f0..c564b925 100644 --- a/src/.vuepress/theme/layouts/Blog.vue +++ b/src/.vuepress/theme/layouts/Blog.vue @@ -128,14 +128,8 @@ export default { return false } - if ( - page.frontmatter && - (page.frontmatter.sitemap ? !page.frontmatter.sitemap.exclude : true) - ) { - result.push({ - ...page, - category: defaultCategory, - }) + if (page.frontmatter && !page.frontmatter.hidden) { + result.push({ ...page, category: defaultCategory }) } }) diff --git a/src/.vuepress/theme/layouts/BlogPost.vue b/src/.vuepress/theme/layouts/BlogPost.vue index 8c954a05..4adb53dd 100644 --- a/src/.vuepress/theme/layouts/BlogPost.vue +++ b/src/.vuepress/theme/layouts/BlogPost.vue @@ -1,5 +1,5 @@