> ## Documentation Index
> Fetch the complete documentation index at: https://dify-6c0370d8-docs-new-agent-experience.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Automatically Publish Plugins via PR

> Automate plugin packaging and Marketplace PR creation with a GitHub Actions workflow that runs on every push to main

## Background

Updating a plugin that others actively use is tedious by hand: modify the code, bump the version, push changes, create a branch, package the file, and open a PR—every release.

**Plugin Auto-PR** is a GitHub Actions workflow that automates the whole sequence. One push packages the plugin and opens the Marketplace PR, so you can focus on building.

## Concepts

### GitHub Actions

GitHub Actions automates development tasks in GitHub. When triggered (for example, by a code push), it runs your workflow in a cloud-based virtual machine, handling everything from build to deployment.

<Frame>
  ![Workflow](https://assets-docs.dify.ai/2025/04/60534de8e220f860947b32a8329a8349.png)
</Frame>

GitHub Actions usage limits:

* **Public repositories**: Unlimited.
* **Private repositories**: 2000 minutes per month.

### Plugin Auto-PR

The workflow runs these steps:

1. Triggers when you push code to the main branch of your plugin source repository.
2. Reads plugin information from the `manifest.yaml` file.
3. Packages the plugin as a `.difypkg` file.
4. Pushes the packaged file to your forked `dify-plugins` repository.
5. Creates a new branch and commits the changes.
6. Creates a PR to merge into the upstream repository.

## Prerequisites

### Repositories

* Your own plugin source code repository (e.g., `your-name/plugin-source`).
* Your own fork of the plugin repository (e.g., `your-name/dify-plugins`).
* The plugin directory structure already in place in your fork:

```text theme={null}
dify-plugins/
└── your-author-name
    └── plugin-name
```

### Permissions

Create a GitHub Personal Access Token (PAT) that can push code to your forked repository and create PRs to the upstream repository.

## Parameters and Configuration

### Setup Requirements

Auto-publishing needs two things in place.

The `manifest.yaml` file drives the automation:

* **`name`**: Your plugin's name (affects package and branch names).
* **`version`**: Semantic version number (increment with each release).
* **`author`**: Your GitHub username (determines repository paths).

The `PLUGIN_ACTION` secret in your plugin source repository holds the credentials:

* **Value**: A Personal Access Token (PAT) with sufficient permissions.
* **Permissions**: Push branches to your forked repository and create PRs to the upstream repository.

### Automatically Generated Parameters

Once set up, the workflow derives these parameters for you:

* **GitHub username**: Read from the `author` field in `manifest.yaml`.
* **Author folder name**: Matches the `author` field.
* **Plugin name**: Read from the `name` field in `manifest.yaml`.
* **Branch name**: `bump-{plugin-name}-plugin-{version}`.
* **Package filename**: `{plugin-name}-{version}.difypkg`.
* **PR title and content**: Generated from the plugin name and version.

## Step-by-Step Guide

<Steps>
  <Step title="Prepare Repositories">
    Ensure you have forked the official `dify-plugins` repository and have your own plugin source repository.
  </Step>

  <Step title="Configure Secret">
    In your plugin source repository, go to **Settings** > **Secrets and variables** > **Actions** > **New repository secret** and create a secret:

    * **Name**: `PLUGIN_ACTION`
    * **Value**: A GitHub Personal Access Token (PAT) with write permissions to the target repository (`your-name/dify-plugins`)

    <Frame>
      ![Create Secrets](https://assets-docs.dify.ai/2025/04/8abd72b677dd24752910c304c76f1c26.png)
    </Frame>
  </Step>

  <Step title="Create Workflow File">
    Create a `.github/workflows/` directory in your repository, add a file named `plugin-publish.yml`, and paste in the workflow below.

    At a high level, the workflow runs on every push to `main`, downloads the Dify Plugin CLI, packages the plugin from the manifest, checks out your `dify-plugins` fork, and opens a PR against `langgenius/dify-plugins` with the new `.difypkg`.

    <Accordion title="Complete workflow YAML" defaultOpen={false}>
      ```yaml theme={null}
      # .github/workflows/plugin-publish.yml
      name: Auto Create PR on Main Push

      on:
        push:
          branches: [ main ]  # Trigger on push to main
        workflow_dispatch:    # Allow manual runs from the Actions tab

      jobs:
        create_pr: # Renamed job for clarity
          runs-on: ubuntu-latest
          steps:
            - name: Checkout code
              uses: actions/checkout@v3

            - name: Print working directory # Kept for debugging
              run: |
                pwd
                ls -la

            - name: Download CLI tool
              run: |
                # Create bin directory in runner temp
                mkdir -p $RUNNER_TEMP/bin
                cd $RUNNER_TEMP/bin

                # Download CLI tool
                wget https://github.com/langgenius/dify-plugin-daemon/releases/latest/download/dify-plugin-linux-amd64
                chmod +x dify-plugin-linux-amd64

                # Show download location and file
                echo "CLI tool location:"
                pwd
                ls -la dify-plugin-linux-amd64

            - name: Get basic info from manifest # Changed step name and content
              id: get_basic_info
              run: |
                PLUGIN_NAME=$(grep "^name:" manifest.yaml | cut -d' ' -f2)
                echo "Plugin name: $PLUGIN_NAME"
                echo "plugin_name=$PLUGIN_NAME" >> $GITHUB_OUTPUT

                VERSION=$(grep "^version:" manifest.yaml | cut -d' ' -f2)
                echo "Plugin version: $VERSION"
                echo "version=$VERSION" >> $GITHUB_OUTPUT

                # If the author's name is not your github username, you can change the author here
                AUTHOR=$(grep "^author:" manifest.yaml | cut -d' ' -f2)
                echo "Plugin author: $AUTHOR"
                echo "author=$AUTHOR" >> $GITHUB_OUTPUT

            - name: Package Plugin
              id: package
              run: |
                # Use the downloaded CLI tool to package
                cd $GITHUB_WORKSPACE
                # Use variables for package name
                PACKAGE_NAME="${{ steps.get_basic_info.outputs.plugin_name }}-${{ steps.get_basic_info.outputs.version }}.difypkg"
                # Use CLI from runner temp
                $RUNNER_TEMP/bin/dify-plugin-linux-amd64 plugin package . -o "$PACKAGE_NAME"

                # Show packaging result
                echo "Package result:"
                ls -la "$PACKAGE_NAME"
                echo "package_name=$PACKAGE_NAME" >> $GITHUB_OUTPUT

                # Show full file path and directory structure (kept for debugging)
                echo "\\nFull file path:"
                pwd
                echo "\\nDirectory structure:"
                tree || ls -R

            - name: Checkout target repo
              uses: actions/checkout@v3
              with:
                # Use author variable for repository
                repository: ${{steps.get_basic_info.outputs.author}}/dify-plugins
                path: dify-plugins
                token: ${{ secrets.PLUGIN_ACTION }}
                fetch-depth: 1 # Fetch only the last commit to speed up checkout
                persist-credentials: true # Persist credentials for subsequent git operations

            - name: Prepare and create PR
              run: |
                # Debug info (kept)
                echo "Debug: Current directory $(pwd)"
                # Use variable for package name
                PACKAGE_NAME="${{ steps.get_basic_info.outputs.plugin_name }}-${{ steps.get_basic_info.outputs.version }}.difypkg"
                echo "Debug: Package name: $PACKAGE_NAME"
                ls -la

                # Move the packaged file to the target directory using variables
                mkdir -p dify-plugins/${{ steps.get_basic_info.outputs.author }}/${{ steps.get_basic_info.outputs.plugin_name }}
                mv "$PACKAGE_NAME" dify-plugins/${{ steps.get_basic_info.outputs.author }}/${{ steps.get_basic_info.outputs.plugin_name }}/

                # Enter the target repository directory
                cd dify-plugins

                # Configure git
                git config user.name "GitHub Actions"
                git config user.email "actions@github.com"

                # Ensure we are on the latest main branch
                git fetch origin main
                git checkout main
                git pull origin main

                # Create and switch to a new branch using variables and new naming convention
                BRANCH_NAME="bump-${{ steps.get_basic_info.outputs.plugin_name }}-plugin-${{ steps.get_basic_info.outputs.version }}"
                git checkout -b "$BRANCH_NAME"

                # Add and commit changes (using git add .)
                git add .
                git status # for debugging
                # Use variables in commit message
                git commit -m "bump ${{ steps.get_basic_info.outputs.plugin_name }} plugin to version ${{ steps.get_basic_info.outputs.version }}"

                # Push to remote (use force just in case the branch existed before from a failed run)
                git push -u origin "$BRANCH_NAME" --force

                # Confirm branch has been pushed and wait for sync (GitHub API might need a moment)
                git branch -a
                echo "Waiting for branch to sync..."
                sleep 10  # Wait 10 seconds for branch sync

            - name: Create PR via GitHub API
              env:
                GH_TOKEN: ${{ secrets.PLUGIN_ACTION }} # Use the provided token for authentication
              run: |
                gh pr create \
                  --repo langgenius/dify-plugins \
                  --head "${{ steps.get_basic_info.outputs.author }}:${{ steps.get_basic_info.outputs.plugin_name }}-${{ steps.get_basic_info.outputs.version }}" \
                  --base main \
                  --title "bump ${{ steps.get_basic_info.outputs.plugin_name }} plugin to version ${{ steps.get_basic_info.outputs.version }}" \
                  --body "bump ${{ steps.get_basic_info.outputs.plugin_name }} plugin package to version ${{ steps.get_basic_info.outputs.version }}

                  Changes:
                  - Updated plugin package file" || echo "PR already exists or creation skipped." # Handle cases where PR already exists

            - name: Print environment info # Kept for debugging
              run: |
                echo "GITHUB_WORKSPACE: $GITHUB_WORKSPACE"
                echo "Current directory contents:"
                ls -R
      ```
    </Accordion>
  </Step>

  <Step title="Update manifest.yaml">
    Make sure these fields are set correctly:

    ```yaml theme={null}
    version: 0.0.x  # Version number
    author: your-github-username  # GitHub username/Author name
    name: your-plugin-name  # Plugin name
    ```
  </Step>
</Steps>

## Usage Guide

### First-Time Setup

Before the first automated release, confirm that:

1. You have forked the official `dify-plugins` repository.
2. Your plugin source repository structure is correct.
3. The `PLUGIN_ACTION` secret is set in your plugin source repository.
4. The workflow file `.github/workflows/plugin-publish.yml` exists.
5. The `name` and `author` fields in `manifest.yaml` are configured correctly.

### Subsequent Updates

To publish new versions after setup:

1. Modify the code.

2. Update the `version` field in `manifest.yaml`.

   <Frame>
     ![Release](https://assets-docs.dify.ai/2025/04/9eed2b9110e91e18008b399e58198f03.png)
   </Frame>

3. Push all changes to the main branch.

4. Wait for GitHub Actions to complete packaging, branch creation, and PR submission.

## Outcome

When you push code to the main branch of your plugin source repository, GitHub Actions runs the publishing process automatically:

* Packages the plugin in `{plugin-name}-{version}.difypkg` format.
* Pushes the packaged file to the target repository.
* Creates a PR to merge into the fork repository.

<Frame>
  ![Outcome](https://assets-docs.dify.ai/2025/04/60d5de910c6ce2482c67ddec3320311f.png)
</Frame>

## Example Repository

See the [example repository](https://github.com/Yevanchen/exa-in-dify) for a working configuration and best practices.
