Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
64 changes: 64 additions & 0 deletions .github/workflows/check-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Check Plugin is ready to submit

on:
workflow_call:
outputs:
has_changelog:
description: 'Whether the PR body contains a changelog section'
value: ${{ jobs.check.outputs.has_changelog }}
changelog:
description: 'Extracted changelog text from the PR body'
value: ${{ jobs.check.outputs.changelog }}

jobs:
check:
name: PR Description Check
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false && github.event.pull_request.user.login != 'dependabot[bot]' && !contains(github.event.pull_request.title, '[skip ci]')
outputs:
has_changelog: ${{ steps.validate.outputs.has_changelog }}
changelog: ${{ steps.validate.outputs.changelog }}

steps:
- name: Check if Auto Submit label is present
id: check-label
uses: actions/github-script@v7
with:
script: |
const labels = context.payload.pull_request.labels || []
const hasSubmitLabel = labels.some(label => label.name === 'Auto submit to Marketplace on merge')
core.setOutput('require_changelog', hasSubmitLabel ? 'true' : 'false')
- name: Checkout tooling
uses: actions/checkout@v4
with:
repository: framer/plugins
path: tooling
sparse-checkout: scripts

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Validate PR body
id: validate
working-directory: tooling
run: node --experimental-strip-types scripts/validate-pr-body.ts
env:
PR_BODY: ${{ github.event.pull_request.body }}
REQUIRE_CHANGELOG: ${{ steps.check-label.outputs.require_changelog }}

- name: Add Auto Submit on merge label
if: >-
steps.validate.outputs.has_changelog == 'true' &&
(github.event.action == 'opened' || github.event.action == 'ready_for_review')
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels: ['Auto submit to Marketplace on merge']
})
54 changes: 3 additions & 51 deletions .github/workflows/shippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,63 +11,15 @@ on:
- unlabeled
workflow_dispatch:
# NOTE: To prevent GitHub from adding PRs to the merge queue before check is done,
# make sure that there is a ruleset that requires the Shippy check to pass.
# make sure that there is a ruleset that requires the "Shippy" check to pass.
merge_group:
types:
- checks_requested

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
check-pr-description:
name: PR Description Check
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false && github.event.pull_request.user.login != 'dependabot[bot]'
steps:
- name: Check if Auto submit to Marketplace on merge label is present
id: check-label
uses: actions/github-script@v7
with:
script: |
const labels = context.payload.pull_request.labels || []
const hasSubmitLabel = labels.some(label => label.name === 'Auto submit to Marketplace on merge')
core.setOutput('require_changelog', hasSubmitLabel ? 'true' : 'false')

- name: Checkout repository
uses: actions/checkout@v4
with:
sparse-checkout: |
scripts
package.json
yarn.lock
.yarnrc.yml
.yarn
.tool-versions

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .tool-versions

- name: Validate PR body
id: validate
run: npx tsx@4.21.0 scripts/validate-pr-body.ts
env:
PR_BODY: ${{ github.event.pull_request.body }}
REQUIRE_CHANGELOG: ${{ steps.check-label.outputs.require_changelog }}

- name: Add Auto submit to Marketplace on merge label
if: >-
steps.validate.outputs.has_changelog == 'true' &&
(github.event.action == 'opened' || github.event.action == 'ready_for_review')
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels: ['Auto submit to Marketplace on merge']
})
uses: ./.github/workflows/check-pr.yml
2 changes: 1 addition & 1 deletion .github/workflows/submit-on-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
- main

jobs:
submit:
submit-on-merge:
name: Submit Changed Plugins
runs-on: ubuntu-latest
# Only run if PR was merged (not just closed) and has "Auto submit to Marketplace on merge" label
Expand Down
62 changes: 46 additions & 16 deletions .github/workflows/submit-plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,20 @@ on:
default: false
type: boolean

# Reusable workflow - can be called from other repos (e.g., framer/workshop)
# Reusable workflow - can be called from other repos
workflow_call:
inputs:
plugin_path:
description: 'Plugin directory (e.g., plugins/csv-import)'
description: 'Plugin directory relative to repo root (e.g., "plugins/csv-import" or ".")'
required: true
type: string
changelog:
description: 'Changelog for this release'
required: true
description: 'Changelog text (mutually exclusive with pr_body)'
required: false
type: string
pr_body:
description: 'Full PR body — changelog will be extracted (mutually exclusive with changelog)'
required: false
type: string
environment:
description: 'Environment (development/production)'
Expand Down Expand Up @@ -63,6 +67,9 @@ on:
SLACK_ERROR_WEBHOOK_URL:
description: 'Slack webhook URL for error notifications'
required: false
PLUGIN_BUILD_SECRETS:
description: 'Newline-separated KEY=VALUE pairs to set as env vars during build'
required: false

jobs:
submit:
Expand All @@ -71,26 +78,38 @@ jobs:
environment: ${{ inputs.environment }}

steps:
- name: Checkout repository
# Checkout the caller's repo (plugin source + git history for tags)
- name: Checkout plugin source
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for git tags and diff
fetch-depth: 0
path: source

# Checkout the plugins repo (scripts + tooling).
# For workflow_dispatch this is redundant (same repo), but keeps the flow uniform.
- name: Checkout tooling
uses: actions/checkout@v4
with:
repository: niekert/plugins
path: tooling

- name: Configure git identity
working-directory: source
run: |
git config --global user.email "marketplace@framer.team"
git config --global user.name "Framer Marketplace"
git config user.email "marketplace@framer.team"
git config user.name "Framer Marketplace"

- name: Validate plugin path
working-directory: source
run: |
if [ ! -d "${{ github.workspace }}/${{ inputs.plugin_path }}" ]; then
if [ ! -d "${{ inputs.plugin_path }}" ]; then
echo "Error: Plugin path '${{ inputs.plugin_path }}' does not exist"
echo ""
echo "Available plugins:"
ls -1 plugins/
echo "Available contents:"
ls -1
exit 1
fi
if [ ! -f "${{ github.workspace }}/${{ inputs.plugin_path }}/framer.json" ]; then
if [ ! -f "${{ inputs.plugin_path }}/framer.json" ]; then
echo "Error: No framer.json found in '${{ inputs.plugin_path }}'"
exit 1
fi
Expand All @@ -99,20 +118,31 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .tool-versions
node-version-file: tooling/.tool-versions

- name: Install dependencies
- name: Install tooling dependencies
working-directory: tooling
run: yarn install

- name: Install source plugin dependencies
working-directory: source
run: yarn install

- name: Build framer-plugin-tools
working-directory: tooling
run: yarn turbo run build --filter=framer-plugin-tools

- name: Set plugin build environment
run: echo "${{ secrets.PLUGIN_BUILD_SECRETS }}" >> "$GITHUB_ENV"

- name: Submit plugin
working-directory: tooling
run: yarn tsx scripts/submit-plugin.ts
env:
PLUGIN_PATH: ${{ github.workspace }}/${{ inputs.plugin_path }}
REPO_ROOT: ${{ github.workspace }}
PLUGIN_PATH: ${{ github.workspace }}/source/${{ inputs.plugin_path }}
REPO_ROOT: ${{ github.workspace }}/source
CHANGELOG: ${{ inputs.changelog }}
PR_BODY: ${{ inputs.pr_body }}
SESSION_TOKEN: ${{ secrets.SESSION_TOKEN }}
FRAMER_ADMIN_SECRET: ${{ secrets.FRAMER_ADMIN_SECRET }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"packageManager": "yarn@4.9.4",
"private": true,
"license": "MIT",
"type": "module",
"workspaces": [
"starters/*",
"packages/*",
Expand Down
3 changes: 2 additions & 1 deletion scripts/lib/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export const BooleanEnvSchema = v.pipe(

export const EnvSchema = v.object({
PLUGIN_PATH: v.pipe(v.string(), v.minLength(1)),
CHANGELOG: v.pipe(v.string(), v.minLength(1)),
CHANGELOG: v.optional(v.pipe(v.string(), v.minLength(1))),
PR_BODY: v.optional(v.string()),
SLACK_WEBHOOK_URL: v.optional(v.string()),
SLACK_ERROR_WEBHOOK_URL: v.optional(v.string()),
RETOOL_URL: v.optional(v.string()),
Expand Down
9 changes: 7 additions & 2 deletions scripts/lib/framer-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,12 @@ export function loadFramerJsonFile(pluginPath: string): FramerJson {
return framerJson
}

export async function submitPlugin(zipFilePath: string, plugin: Plugin, env: Environment): Promise<SubmissionResponse> {
export async function submitPlugin(
zipFilePath: string,
plugin: Plugin,
env: Environment,
changelog: string
): Promise<SubmissionResponse> {
if (!env.SESSION_TOKEN || !env.FRAMER_ADMIN_SECRET) {
throw new Error("Session token and Framer admin secret are required for submission")
}
Expand All @@ -125,7 +130,7 @@ export async function submitPlugin(zipFilePath: string, plugin: Plugin, env: Env

const formData = new FormData()
formData.append("file", blob, "plugin.zip")
formData.append("content", await changelogToHtml(env.CHANGELOG))
formData.append("content", await changelogToHtml(changelog))

const response = await fetch(url, {
method: "POST",
Expand Down
5 changes: 2 additions & 3 deletions scripts/lib/git.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { execFileSync } from "node:child_process"
import type { Environment } from "./env"
import { log } from "./logging"

export function createGitTag(pluginName: string, version: number, repoRoot: string, env: Environment): void {
export function createGitTag(pluginName: string, version: number, repoRoot: string, changelog: string): void {
const tagName = `${pluginName.toLowerCase().replace(/\s+/g, "-")}-v${version.toString()}`

log.info(`Creating git tag: ${tagName}`)
Expand All @@ -16,7 +15,7 @@ export function createGitTag(pluginName: string, version: number, repoRoot: stri
// Tag doesn't exist, that's fine
}

execFileSync("git", ["tag", "-a", tagName, "-m", env.CHANGELOG.trim()], {
execFileSync("git", ["tag", "-a", tagName, "-m", changelog.trim()], {
cwd: repoRoot,
stdio: "inherit",
})
Expand Down
5 changes: 3 additions & 2 deletions scripts/lib/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ interface SlackWorkflowPayload {
export async function sendSlackNotification(
framerJson: FramerJson,
submissionResult: SubmissionResponse,
env: Environment
env: Environment,
changelog: string
): Promise<void> {
const payload: SlackWorkflowPayload = {
pluginName: framerJson.name,
pluginVersion: submissionResult.version.toString(),
marketplacePreviewUrl: `${getURL(env, "marketplaceBaseUrl")}/plugins/${submissionResult.slug}/preview`,
pluginReviewUrl: `${getURL(env, "framerAppUrl")}/projects/new?plugin=${submissionResult.internalPluginId}&pluginVersion=${submissionResult.versionId}`,
changelog: env.CHANGELOG,
changelog: changelog,
retoolUrl: env.RETOOL_URL,
}

Expand Down
Loading
Loading