Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
7b97396
chore: wip
nickytonline Aug 30, 2022
19fa8e6
chore: switched to ES2022 for TS
nickytonline Aug 30, 2022
edcb194
chore: got query returning actual authors instead of refs
nickytonline Aug 30, 2022
7bb4943
chore: moved back to TypeScript because existing tests aren't current…
nickytonline Aug 30, 2022
a560cfd
test: added tests for getSanityPluginLookup
nickytonline Aug 30, 2022
a059c6d
chore: added a new line at end of gitignore
nickytonline Aug 30, 2022
a01a086
chore: added getPluginDiffsForSanity
nickytonline Aug 30, 2022
78db1b0
chore: refactor to avoid passing in a Promise
nickytonline Aug 31, 2022
836ec22
chore: removed commented out dead code
nickytonline Aug 31, 2022
6d62bb8
chore: fix type import for SanityClient
nickytonline Aug 31, 2022
1f04cf3
chore: added GH action to synch plugins to CMS
nickytonline Aug 31, 2022
b32b988
chore: added some TODOs for going to production with the sync to CMS …
nickytonline Aug 31, 2022
0fa5977
chore: cleaning up the script a bit
nickytonline Aug 31, 2022
b0101df
chore: added _id field to query so it can be used to retrieve full ob…
nickytonline Sep 1, 2022
b80c77a
chore: now only number of diffs is logged instead of payload
nickytonline Sep 1, 2022
6985148
chore: first iteration of updating CMS working
nickytonline Sep 2, 2022
a92eedb
chore: plugin sync to CMS now includes plugin compatibility property
nickytonline Sep 2, 2022
036dfad
chore: fixed some logic/tests
nickytonline Sep 2, 2022
19a2b31
chore: added some logging around plugin diffs found
nickytonline Sep 2, 2022
de0661b
chore: fixed some issues around plugins sync in regards to authors
nickytonline Sep 2, 2022
8751c55
chore: improved log messaging
nickytonline Sep 2, 2022
4dedfa6
chore: added some comments and more types
nickytonline Sep 2, 2022
dd21118
chore: renamed synchronization script
nickytonline Sep 2, 2022
d6e60af
chore: removed dryrun config used for debugging purposes
nickytonline Sep 2, 2022
8ef77b4
chore: updated a type
nickytonline Sep 2, 2022
5be7bf7
chore: cleaned up some comments
nickytonline Sep 2, 2022
905be79
chore: promisises from fs is aliased now
nickytonline Sep 2, 2022
55b1c0d
chore: fixed a type typo
nickytonline Sep 2, 2022
314e47f
Merge remote-tracking branch 'origin/main' into nickytonline/sync-plu…
nickytonline Sep 26, 2022
599099d
chore: updated logic for compatibility field now that CMS schema has …
nickytonline Sep 26, 2022
06ce3e5
chore: updated comment on how to run script for local development
nickytonline Sep 26, 2022
8e82935
chore: added the Sanity uuid package to generate _key properties for …
nickytonline Sep 26, 2022
b155978
chore: clean up diff logic for plugins to Sanity sync
nickytonline Sep 26, 2022
780d0c9
chore: adds a _key field to compatibility items as required by Sanity
nickytonline Sep 26, 2022
c95dbd0
test: updated tests
nickytonline Sep 26, 2022
aa84c9f
chore: removed unused fields for sync
nickytonline Sep 26, 2022
08e4004
chore: added some more comments
nickytonline Sep 26, 2022
623313d
chore: stripped _key property for compatibility array comparison
nickytonline Sep 27, 2022
05d93ec
test: updated test to ensure _key properties are in compatibility items
nickytonline Sep 27, 2022
7859001
chore: removed environment config from sync GitHub action
nickytonline Sep 27, 2022
c0f3829
Merge remote-tracking branch 'origin/main' into nickytonline/sync-plu…
nickytonline Sep 27, 2022
900b903
chore: removed running sync HG action on PR changes
nickytonline Sep 27, 2022
3db0190
chore: added a guard to not run repo to cms sync if cms_sync PR label…
nickytonline Sep 29, 2022
3dc374e
chore: added a comment about the GH action guard
nickytonline Sep 29, 2022
51cdb27
Merge remote-tracking branch 'origin/main' into nickytonline/sync-plu…
nickytonline Oct 4, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/sync-to-cms.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Sync Plugins to CMS
on:
pull_request:
types:
- closed
jobs:
sync-to-cms:
# Only run if the merged PR wasn't an automated PR for synching from the cms to the repo
if: github.event.pull_request.merged && !contains(github.event.pull_request.labels.*.name, 'cms_sync')
Comment on lines +3 to +9
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@estephinson @tzmanics, this is the guard to prevent a potential infinite loop of sync repo to cms/cms to repo I mentioned. Ed or whoever will need to reapprove the PR.

runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v3
- name: Using Node.js
uses: actions/setup-node@v3
with:
node-version: '*'
cache: 'npm'
check-latest: true
- name: Install dependencies
run: npm install
- name: Sync plugins to CMS
env:
SANITY_API_TOKEN: ${{ secrets.SANITY_API_TOKEN }}
SANITY_PROJECT_ID: ${{ secrets.SANITY_PROJECT_ID }}
SANITY_DATASET: ${{ secrets.SANITY_DATASET }}
run: npx tsx bin/sync_plugins_to_cms.js
137 changes: 137 additions & 0 deletions bin/sync_plugins_to_cms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// eslint-env node
/* eslint-disable n/prefer-global/process */
import { promises as fs } from 'fs'
import path from 'path'

import sanityClient from '@sanity/client'
import { uuid } from '@sanity/uuid'

// when testing this script locally, add a path in your .env for GITHUB_WORKSPACE or pass it in
// e.g. GITHUB_WORKSPACE="$(pwd)" npx tsx bin/sync_plugins_to_cms.js

/**
* @typedef { import("../types/plugins").SanityBuildPluginEntity } SanityBuildPluginEntity
* @typedef { import("@sanity/client").SanityClient } SanityClient
* @typedef { import("@sanity/client").Transaction } Transaction
* @typedef { import("@sanity/client").Patch } Patch
* @typedef { import("../types/plugins").BuildPluginEntity } BuildPluginEntity
*/

import { getPluginDiffsForSanity, getSanityPluginLookup } from './utils.js'

if (process.env.NODE_ENV === 'development') {
// Using dotenv for local development.
console.log('running in development mode')

const dotenv = await import('dotenv')
dotenv.config()
}

const { GITHUB_WORKSPACE, SANITY_API_TOKEN, SANITY_PROJECT_ID, SANITY_DATASET } = process.env
const [apiVersion] = new Date().toISOString().split('T')

const config = {
projectId: SANITY_PROJECT_ID,
dataset: SANITY_DATASET,
apiVersion,
token: SANITY_API_TOKEN,
// make sure we have the freshest data when doing the diff with plugins.json
useCdn: false,
}

/**
* Creates a transaction containing updates to plugins for the CMS
*
* @param {Transaction} transaction
* @param {Patch} patch
* @param {BuildPluginEntity[]} diffs
* @returns
*/
const createUpdates = (transaction, patch, diffs) =>
diffs.reduce((tx, plugin) => {
const { _id, ...changes } = plugin
const fieldUpdates = {}
const fieldRemovals = []

for (const [key, value] of Object.entries(changes)) {
// any property that is null needs to be unset instead of being set to null
if (value === null) {
fieldRemovals.push(key)
} else {
fieldUpdates[key] = value
}
}

const update = patch(_id).set(fieldUpdates)

if (fieldRemovals.length !== 0) {
update.unset(fieldRemovals)
}

tx.patch(update)

return tx
}, transaction)

/**
* @type {SanityClient}
*/
const client = sanityClient(config)

// These are the only fields to synch for the moment.
const query = `*[_type == "buildPlugin"] {
_id,
packageName,
version,
compatibility[]
}`

// TODO: Add a retry mechanism to handle network errors
try {
const pluginsFilePath = path.join(GITHUB_WORKSPACE, '/site/plugins.json')
const fileContents = await fs.readFile(pluginsFilePath)
const plugins = JSON.parse(fileContents).map((plugin) => {
// Ensure if a compatibility field exists, that it has all the necessary fields to sync with Sanity
if (plugin.compatibility) {
// eslint-disable-next-line no-param-reassign
plugin.compatibility = plugin.compatibility.map((compatibilityItem) => {
const updatedCompatibilityItem = {
// A _key property is required by Sanity so each array item can be identified in a collaborative way
// See https://www.sanity.io/docs/array-type#92296c6c45ea
_key: uuid(),
...compatibilityItem,
}

return updatedCompatibilityItem
})
}

return plugin
})

console.info('Detecting plugin changes...')

/**
* @type {SanityBuildPluginEntity[]}
*/
const sanityBuildPlugins = await client.fetch(query, {})
const sanityPluginLookup = await getSanityPluginLookup(sanityBuildPlugins)
const pluginDiffs = getPluginDiffsForSanity(sanityPluginLookup, plugins)

if (pluginDiffs.length === 0) {
console.info('No plugin changes found.')
} else {
console.info(`Found ${pluginDiffs.length} plugins with changes`)
console.info('Updating plugins...')

const transaction = createUpdates(client.transaction(), client.patch, pluginDiffs)

client.mutate(transaction)
console.info('Plugins were updated in the CMS.')
}
} catch (error) {
console.error(error)
throw new Error('Unable to synchronize plugins to the CMS')
}

/* eslint-enable n/prefer-global/process */
101 changes: 101 additions & 0 deletions bin/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */

/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */

/* Language and Environment */
"target": "ES2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */

/* Modules */
"module": "ES2022" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */

/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */

/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */

/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */

/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
Loading