Skip to content

Commit 12b3e1d

Browse files
authored
Merge branch 'main' into main
2 parents d70547f + d690311 commit 12b3e1d

22 files changed

+9607
-4351
lines changed

.github/CODEOWNERS

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
* @netlify/netlify-dev @netlify/workflow
2-
docs/ @netlify/docs
1+
* @netlify/ecosystem-pod-integrations
2+
docs/ @netlify/department-docs

.github/workflows/fossa.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
runs-on: ubuntu-latest
1616
steps:
1717
- name: Checkout
18-
uses: actions/checkout@v3
18+
uses: actions/checkout@v4
1919
- name: Download fossa cli
2020
run: |-
2121
mkdir -p $HOME/.local/bin

.github/workflows/release-please.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@ jobs:
77
release-please:
88
runs-on: ubuntu-latest
99
steps:
10-
- uses: navikt/github-app-token-generator@a9cd374e271b8aef573b8c16ac46c44fb89b02db
10+
- uses: navikt/github-app-token-generator@a8ae52448279d468cfbca5cd899f2457f0b1f643
1111
id: get-token
1212
with:
1313
private-key: ${{ secrets.TOKENS_PRIVATE_KEY }}
1414
app-id: ${{ secrets.TOKENS_APP_ID }}
15-
- uses: GoogleCloudPlatform/release-please-action@v3
15+
- uses: GoogleCloudPlatform/release-please-action@v4
1616
id: release
1717
with:
1818
token: ${{ steps.get-token.outputs.token }}
1919
release-type: node
2020
package-name: '@netlify/plugins-list'
21-
- uses: actions/checkout@v3
21+
- uses: actions/checkout@v4
2222
if: ${{ steps.release.outputs.release_created }}
23-
- uses: actions/setup-node@v3
23+
- uses: actions/setup-node@v4
2424
with:
2525
node-version: '*'
2626
cache: 'npm'
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Sync CMS to Plugins Repository
2+
on:
3+
repository_dispatch:
4+
# sync_cms_to_repo is a bespoke type created for use with the CMS Webhook
5+
types: [sync_cms_to_repo]
6+
jobs:
7+
sync-to-repo:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Git checkout
11+
uses: actions/checkout@v4
12+
- name: Using Node.js
13+
uses: actions/setup-node@v4
14+
with:
15+
node-version: '*'
16+
cache: 'npm'
17+
check-latest: true
18+
- name: Install dependencies
19+
run: npm install
20+
- name: Setup git config
21+
run: |
22+
git config user.name 'token-generator-app[bot]'
23+
git config user.email '82042599+token-generator-app[bot]@users.noreply.github.com'
24+
- name: Generate GitHub token
25+
uses: navikt/github-app-token-generator@v1.2.1
26+
id: get-token
27+
with:
28+
private-key: ${{ secrets.TOKENS_PRIVATE_KEY }}
29+
app-id: ${{ secrets.TOKENS_APP_ID }}
30+
- name: Sync CMS to repo
31+
env:
32+
GITHUB_TOKEN: ${{ steps.get-token.outputs.token }}
33+
CMS_CHANGES: ${{ toJson(github.event.client_payload) }}
34+
35+
run: bin/sync_cms_to_repo.sh

.github/workflows/sync-to-cms.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Sync Plugins to CMS
2+
on:
3+
pull_request:
4+
types:
5+
- closed
6+
jobs:
7+
sync-to-cms:
8+
# Only run if the merged PR wasn't an automated PR for synching from the cms to the repo
9+
if: github.event.pull_request.merged && !contains(github.event.pull_request.labels.*.name, 'cms_sync')
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Git checkout
13+
uses: actions/checkout@v4
14+
- name: Using Node.js
15+
uses: actions/setup-node@v4
16+
with:
17+
node-version: '*'
18+
cache: 'npm'
19+
check-latest: true
20+
- name: Install dependencies
21+
run: npm install
22+
- name: Sync plugins to CMS
23+
env:
24+
SANITY_API_TOKEN: ${{ secrets.SANITY_API_TOKEN }}
25+
SANITY_PROJECT_ID: ${{ secrets.SANITY_PROJECT_ID }}
26+
SANITY_DATASET: ${{ secrets.SANITY_DATASET }}
27+
run: npx tsx bin/sync_plugins_to_cms.js

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ jobs:
1212
timeout-minutes: 30
1313
steps:
1414
- name: Git checkout
15-
uses: actions/checkout@v3
15+
uses: actions/checkout@v4
1616
- name: Using Node.js
17-
uses: actions/setup-node@v3
17+
uses: actions/setup-node@v4
1818
with:
1919
node-version: '*'
2020
cache: 'npm'

.github/workflows/versioning.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
runs-on: ubuntu-latest
99
steps:
1010
- name: Git checkout
11-
uses: actions/checkout@v3
11+
uses: actions/checkout@v4
1212
# Updates the list-v* git tag to version `plugins.json`.
1313
# Consumers must use the tag-specific deploy URL:
1414
# list-v*--netlify-plugins.netlify.app/plugins.json

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ node_modules
1010
/build
1111
.vscode
1212
.DS_Store
13+
.env
14+
15+
# Local Netlify folder
16+
.netlify

CHANGELOG.md

Lines changed: 579 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,6 @@
22

33
[Build Plugins](https://docs.netlify.com/configure-builds/build-plugins) are a new way to extend the functionality of your build on Netlify. The [`plugins.json` file](./site/plugins.json) in this repository is used to generate the [Netlify plugins directory](https://app.netlify.com/plugins). Plugins in this directory can be installed directly through the Netlify UI.
44

5-
## Contributing
6-
7-
The Netlify Plugins directory is filled with plugins created by Netlify staff and members of the community like you. You can contribute to the directory in the following ways:
8-
9-
- [Add a plugin](./docs/CONTRIBUTING.md#add-a-plugin) you've written to the plugins directory.
10-
- [Update a plugin](./docs/CONTRIBUTING.md#update-a-plugin) you maintain that's already in the directory.
11-
- [Request directory deactivation](./docs/CONTRIBUTING.md#request-deactivation) for a plugin which is not being maintained.
12-
13-
Visit the repository [contributor guide](./docs/CONTRIBUTING.md) for details.
14-
15-
## Plugin review
16-
17-
If you're reviewing someone else's plugin, please check the following [document](docs/plugin_review.md).
18-
195
## Code of Conduct
206

217
This project and everyone participating in it is governed by a [code of conduct](./docs/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to conduct@netlify.com.

bin/sync_cms_to_plugins_repo.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { promises as fs } from 'fs'
2+
3+
import plugins from '../site/plugins.json'
4+
5+
import { updatePlugins } from './utils.js'
6+
7+
// eslint-disable-next-line n/prefer-global/process
8+
const changes = JSON.parse(process.env.CMS_CHANGES)
9+
10+
console.log('Checking for CMS updates...')
11+
console.log('Changes to synchronize', changes)
12+
console.log('Synchronizing changes to plugins repo...')
13+
const updatedPlugins = updatePlugins(changes, plugins)
14+
fs.writeFile('site/plugins.json', `${JSON.stringify(updatedPlugins, null, 2)}\n`)
15+
16+
console.log('Done synching CMS updates to plugins repo.')

bin/sync_cms_to_repo.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
PR_TITLE="chore: cms to repo sync"
2+
BRANCH_NAME="sync_cms_to_plugins_$(date +%s)"
3+
4+
git branch $BRANCH_NAME
5+
git switch $BRANCH_NAME
6+
7+
echo "Syncing CMS to plugins"
8+
npx tsx bin/sync_cms_to_plugins_repo.js
9+
10+
11+
# This is the only file we want to commit
12+
git add site/plugins.json
13+
14+
# See if we have any changes. We should.
15+
if [[ -n "$(git status --porcelain)" ]]; then
16+
echo "Creating PR \"$PR_TITLE\" for branch $BRANCH_NAME"
17+
git commit -m "$PR_TITLE"
18+
git push origin $BRANCH_NAME
19+
gh pr create --title "$PR_TITLE" --body "This is an automated PR to sync the CMS to the repo" --label "cms_sync" --label "automerge"
20+
else
21+
# Shouldn't end up here, but log that there was nothing to sync
22+
echo "Looks like there was nothing to sync."
23+
fi

bin/sync_plugins_to_cms.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// eslint-env node
2+
/* eslint-disable n/prefer-global/process */
3+
import { promises as fs } from 'fs'
4+
import path from 'path'
5+
6+
import sanityClient from '@sanity/client'
7+
import { uuid } from '@sanity/uuid'
8+
9+
// when testing this script locally, add a path in your .env for GITHUB_WORKSPACE or pass it in
10+
// e.g. GITHUB_WORKSPACE="$(pwd)" npx tsx bin/sync_plugins_to_cms.js
11+
12+
/**
13+
* @typedef { import("../types/plugins").SanityBuildPluginEntity } SanityBuildPluginEntity
14+
* @typedef { import("@sanity/client").SanityClient } SanityClient
15+
* @typedef { import("@sanity/client").Transaction } Transaction
16+
* @typedef { import("@sanity/client").Patch } Patch
17+
* @typedef { import("../types/plugins").BuildPluginEntity } BuildPluginEntity
18+
*/
19+
20+
import { getPluginDiffsForSanity, getSanityPluginLookup } from './utils.js'
21+
22+
if (process.env.NODE_ENV === 'development') {
23+
// Using dotenv for local development.
24+
console.log('running in development mode')
25+
26+
const dotenv = await import('dotenv')
27+
dotenv.config()
28+
}
29+
30+
const { GITHUB_WORKSPACE, SANITY_API_TOKEN, SANITY_PROJECT_ID, SANITY_DATASET } = process.env
31+
const [apiVersion] = new Date().toISOString().split('T')
32+
33+
const config = {
34+
projectId: SANITY_PROJECT_ID,
35+
dataset: SANITY_DATASET,
36+
apiVersion,
37+
token: SANITY_API_TOKEN,
38+
// make sure we have the freshest data when doing the diff with plugins.json
39+
useCdn: false,
40+
}
41+
42+
/**
43+
* Creates a transaction containing updates to plugins for the CMS
44+
*
45+
* @param {Transaction} transaction
46+
* @param {Patch} patch
47+
* @param {BuildPluginEntity[]} diffs
48+
* @returns
49+
*/
50+
const createUpdates = (transaction, patch, diffs) =>
51+
diffs.reduce((tx, plugin) => {
52+
const { _id, ...changes } = plugin
53+
const fieldUpdates = {}
54+
const fieldRemovals = []
55+
56+
for (const [key, value] of Object.entries(changes)) {
57+
// any property that is null needs to be unset instead of being set to null
58+
if (value === null) {
59+
fieldRemovals.push(key)
60+
} else {
61+
fieldUpdates[key] = value
62+
}
63+
}
64+
65+
const update = patch(_id).set(fieldUpdates)
66+
67+
if (fieldRemovals.length !== 0) {
68+
update.unset(fieldRemovals)
69+
}
70+
71+
tx.patch(update)
72+
73+
return tx
74+
}, transaction)
75+
76+
/**
77+
* @type {SanityClient}
78+
*/
79+
const client = sanityClient(config)
80+
81+
// These are the only fields to synch for the moment.
82+
const query = `*[_type == "buildPlugin"] {
83+
_id,
84+
packageName,
85+
version,
86+
compatibility[]
87+
}`
88+
89+
// TODO: Add a retry mechanism to handle network errors
90+
try {
91+
const pluginsFilePath = path.join(GITHUB_WORKSPACE, '/site/plugins.json')
92+
const fileContents = await fs.readFile(pluginsFilePath)
93+
const plugins = JSON.parse(fileContents).map((plugin) => {
94+
// Ensure if a compatibility field exists, that it has all the necessary fields to sync with Sanity
95+
if (plugin.compatibility) {
96+
// eslint-disable-next-line no-param-reassign
97+
plugin.compatibility = plugin.compatibility.map((compatibilityItem) => {
98+
const updatedCompatibilityItem = {
99+
// A _key property is required by Sanity so each array item can be identified in a collaborative way
100+
// See https://www.sanity.io/docs/array-type#92296c6c45ea
101+
_key: uuid(),
102+
...compatibilityItem,
103+
}
104+
105+
return updatedCompatibilityItem
106+
})
107+
}
108+
109+
return plugin
110+
})
111+
112+
console.info('Detecting plugin changes...')
113+
114+
/**
115+
* @type {SanityBuildPluginEntity[]}
116+
*/
117+
const sanityBuildPlugins = await client.fetch(query, {})
118+
const sanityPluginLookup = await getSanityPluginLookup(sanityBuildPlugins)
119+
const pluginDiffs = getPluginDiffsForSanity(sanityPluginLookup, plugins)
120+
121+
if (pluginDiffs.length === 0) {
122+
console.info('No plugin changes found.')
123+
} else {
124+
console.info(`Found ${pluginDiffs.length} plugins with changes`)
125+
console.info('Updating plugins...')
126+
127+
const transaction = createUpdates(client.transaction(), client.patch, pluginDiffs)
128+
129+
client.mutate(transaction)
130+
console.info('Plugins were updated in the CMS.')
131+
}
132+
} catch (error) {
133+
console.error(error)
134+
throw new Error('Unable to synchronize plugins to the CMS')
135+
}
136+
137+
/* eslint-enable n/prefer-global/process */

0 commit comments

Comments
 (0)