### Example GitHub Actions Workflow for Plugin Release Source: https://github.com/flow-launcher/flow.launcher.pluginsmanifest/blob/main/README.md An example of a GitHub Actions workflow file used for automating the build and release process of a Flow Launcher plugin. This workflow is a requirement for plugin submissions and ensures consistent deployment. ```yaml # This is an example workflow, actual content may vary. # Refer to the documentation for specific implementation details. name: Publish Release on: push: tags: - 'v*.*.* jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v3 with: python-version: '3.x' - name: Install dependencies run: | python -m pip install --upgrade pip pip install build - name: Build plugin run: python -m build --sdist --wheel --outdir dist/ . - name: Create GitHub Release id: create_release uses: actions/create-release@v1 with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} draft: false prerelease: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload Release Asset uses: actions/upload-release-asset@v1 with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: ./dist/${{ github.event.repository.name }}-${{ github.ref_name }}.zip # Example asset path, adjust as needed asset_name: ${{ github.event.repository.name }}-${{ github.ref_name }}.zip asset_content_type: application/zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` -------------------------------- ### Read Plugin Manifests using Python Source: https://context7.com/flow-launcher/flow.launcher.pluginsmanifest/llms.txt The `plugin_reader()` function from the `_utils` module scans the designated plugins directory and loads all plugin manifest files (in JSON format) into memory. It returns a list of dictionaries, where each dictionary represents a plugin's metadata. This list can be iterated over to access individual plugin details or filtered based on specific criteria like language or ID. ```python from _utils import plugin_reader, plugin_dir # Read all plugin manifests plugin_infos = plugin_reader() # Access plugin information for plugin in plugin_infos: print(f"Plugin: {plugin['Name']}") print(f"Version: {plugin['Version']}") print(f"Author: {plugin['Author']}") print(f"Download: {plugin['UrlDownload']}") print("---") # Filter plugins by language python_plugins = [p for p in plugin_infos if p['Language'] == 'python'] print(f"Total Python plugins: {len(python_plugins)}") # Find specific plugin by ID target_id = "85eb90ff-b92d-476b-bae4-3d71398e61f9" plugin = next((p for p in plugin_infos if p['ID'] == target_id), None) if plugin: print(f"Found plugin: {plugin['Name']}") ``` -------------------------------- ### Write Plugin Manifests using Python Source: https://context7.com/flow-launcher/flow.launcher.pluginsmanifest/llms.txt The `plugin_writer()` function from the `_utils` module is used to save updated plugin manifest data back to their respective JSON files in the plugins directory. It takes a list of plugin dictionaries as input and writes each one to a file named `{PluginName}-{UUID}.json`. This function is crucial for persisting changes made to plugin metadata, such as version updates. ```python from _utils import plugin_reader, plugin_writer, plugin_name, id_name, version # Read existing plugins plugins = plugin_reader() # Update a plugin version for plugin in plugins: if plugin['Name'] == '7TV Emotes': plugin['Version'] = '0.0.4' plugin['UrlDownload'] = 'https://github.com/WaterBoiledPizza/7TV-Emotes/releases/download/v0.0.4/Flow.Launcher.Plugin.7tvEmotesv0.0.4.zip' plugin['LatestReleaseDate'] = '2023-03-15T10:00:00Z' break # Write all plugins back to files plugin_writer(plugins) # Each plugin is written to: plugins/{Name}-{ID}.json # Example: plugins/7TV Emotes-85eb90ff-b92d-476b-bae4-3d71398e61f9.json ``` -------------------------------- ### Python ETag Caching for GitHub API Source: https://context7.com/flow-launcher/flow.launcher.pluginsmanifest/llms.txt This system manages HTTP ETags to optimize GitHub API requests by leveraging caching. It reads and writes ETag values to a file, allowing the application to send `If-None-Match` headers. This avoids redundant requests when data has not changed, resulting in saved bandwidth and API rate limits. Includes functionality to remove stale ETags. ```python from _utils import etag_reader, etags_writer, etag_file import json # Read current ETags def load_etags(): etags = etag_reader() # Returns: {"plugin-uuid-1": "\"etag-hash-1\"", "plugin-uuid-2": "\"etag-hash-2\""} return etags # Update ETags after checking for updates def update_etags(): etags = { "85eb90ff-b92d-476b-bae4-3d71398e61f9": "\"W/abc123def456\"", "2D0AFF97E5A445E3BEDD1081B811075A": "\"W/xyz789uvw012\"" } etags_writer(etags) print(f"Saved {len(etags)} ETags to {etag_file}") # Remove ETags for deleted plugins def cleanup_etags(): from updater import remove_unused_etags from _utils import plugin_reader current_etags = etag_reader() current_plugins = plugin_reader() cleaned_etags = remove_unused_etags(current_plugins, current_etags) removed_count = len(current_etags) - len(cleaned_etags) if removed_count > 0: etags_writer(cleaned_etags) print(f"Removed {removed_count} unused ETags") else: print("No unused ETags found") # Example ETag usage in GitHub API request: # headers = {"If-None-Match": etags.get(plugin_id, "")} # response = requests.get(api_url, headers=headers) # if response.status_code == 304: # print("Plugin not modified, using cached data") ``` -------------------------------- ### Python: URL Validation and Utility Functions Source: https://context7.com/flow-launcher/flow.launcher.pluginsmanifest/llms.txt Provides helper functions for string manipulation and validation within the Flow Launcher plugin system. Includes cleaning version strings, comparing versions as tuples, validating URLs, and parsing GitHub download URLs using regular expressions. These functions are crucial for maintaining data integrity and automating plugin management. ```python from _utils import ( clean, version_tuple, check_url, github_download_url_regex, get_new_plugin_submission_ids ) # Clean version strings version = "v1.2.3" cleaned = clean(version, "v") # Returns: "1.2.3" # Compare versions as tuples v1 = version_tuple("v1.2.3") # Returns: ("1", "2", "3") v2 = version_tuple("v1.3.0") # Returns: ("1", "3", "0") is_newer = v2 > v1 # True # Validate URLs valid_url = "https://cdn.jsdelivr.net/gh/user/repo@main/icon.png" is_valid = check_url(valid_url) # Returns: True invalid_url = "not-a-url" is_valid = check_url(invalid_url) # Returns: False # Parse GitHub download URLs download_url = "https://github.com/WaterBoiledPizza/7TV-Emotes/releases/download/v0.0.3/Flow.Launcher.Plugin.7tvEmotesv0.0.3.zip" match = github_download_url_regex.match(download_url) if match: author = match.group('author') # "WaterBoiledPizza" repo = match.group('repo') # "7TV-Emotes" version = match.group('version') # "v0.0.3" filename = match.group('filename') # "Flow.Launcher.Plugin.7tvEmotesv0.0.3" print(f"Parsed: {author}/{repo} @ {version}") # Detect new plugin submissions new_ids = get_new_plugin_submission_ids() print(f"New submissions: {new_ids}") # Returns list of plugin IDs that exist in plugins/ but not in plugins.json ``` -------------------------------- ### Automate Flow Launcher Plugin Updates Source: https://context7.com/flow-launcher/flow.launcher.pluginsmanifest/llms.txt The `updater.py` script automates the process of updating Flow Launcher plugins. It fetches the latest release information from GitHub, downloads updated metadata from plugin zip files, and updates version information. It utilizes ETag caching to avoid redundant downloads and can optionally send Discord notifications. Dependencies include `asyncio`, `aiohttp`, and custom modules like `updater` and `_utils`. ```python import asyncio from updater import batch_plugin_infos, remove_unused_etags from _utils import plugin_reader, plugin_writer, etag_reader, etags_writer async def update_plugins(github_token, webhook_url=None): # Read current plugin manifests and ETags for caching plugin_infos = plugin_reader() etags = etag_reader() # Batch update all plugins with GitHub API # ETags prevent unnecessary downloads when no updates exist updated_plugins = await batch_plugin_infos( plugin_infos, etags, github_token, webhook_url ) # Save updated plugin manifests plugin_writer(updated_plugins) # Clean up ETags for removed plugins etags_new = remove_unused_etags(updated_plugins, etags) etags_writer(etags_new) print(f"Updated {len(updated_plugins)} plugins") # Run the updater # asyncio.run(update_plugins('ghp_token_here', 'https://discord.webhook.url')) ``` -------------------------------- ### Python Script to Merge Plugin Manifests Source: https://context7.com/flow-launcher/flow.launcher.pluginsmanifest/llms.txt The `merge-manifest.py` script aggregates individual plugin manifest files into a single `plugins.json` file. This consolidated file is used by Flow Launcher to populate its plugin store. It supports merging all plugins or only newly submitted ones for testing. ```python from merge_manifest import get_all_plugins, get_new_plugins, save_plugins_json_file import sys # Merge all plugins into plugins.json def merge_all_plugins(): all_plugins = get_all_plugins() save_plugins_json_file(all_plugins) print(f"Merged {len(all_plugins)} plugins into plugins.json") # Merge only newly submitted plugins (for testing) def merge_new_plugins_only(): new_plugins = get_new_plugins() save_plugins_json_file(new_plugins) print(f"Merged {len(new_plugins)} new plugins") for plugin in new_plugins: print(f" - {plugin['Name']} by {plugin['Author']}") # Command line usage # python merge-manifest.py # Merges all plugins # python merge-manifest.py new-only # Merges only new submissions # Example output structure in plugins.json: # [ # {"ID": "...", "Name": "Plugin 1", ...}, # {"ID": "...", "Name": "Plugin 2", ...}, # ... # ] ``` -------------------------------- ### Plugin Manifest JSON Structure Source: https://context7.com/flow-launcher/flow.launcher.pluginsmanifest/llms.txt This JSON structure defines the metadata for a Flow Launcher plugin. It includes details such as the plugin's unique ID, name, description, author, version, language, website, icon path, source code URL, download URL, testing status, and version history dates. This format is used for each individual plugin manifest file. ```json { "ID": "85eb90ff-b92d-476b-bae4-3d71398e61f9", "Name": "7TV Emotes", "Description": "Search emotes from 7TV", "Author": "Water Boiled Pizza", "Version": "0.0.3", "Language": "python", "Website": "https://github.com/WaterBoiledPizza/7TV-Emotes", "IcoPath": "https://cdn.jsdelivr.net/gh/WaterBoiledPizza/7TV-Emotes@main/icon.png?raw=true", "UrlSourceCode": "https://github.com/WaterBoiledPizza/7TV-Emotes", "UrlDownload": "https://github.com/WaterBoiledPizza/7TV-Emotes/releases/download/v0.0.3/Flow.Launcher.Plugin.7tvEmotesv0.0.3.zip", "Tested": true, "DateAdded": "2023-03-12T07:36:16Z", "LatestReleaseDate": "2023-03-13T13:03:44Z", "MinimumAppVersion": "2.0.0" } ``` -------------------------------- ### GitHub Actions: Auto Update Workflow Source: https://context7.com/flow-launcher/flow.launcher.pluginsmanifest/llms.txt Automates the process of checking for plugin updates, merging manifest files, and committing changes. This workflow runs on a schedule every 3 hours and uses GitHub secrets for authentication and Discord webhooks for notifications. It requires Python 3.x and specific dependencies listed in requirements-updater.txt. ```yaml name: Auto Update on: workflow_dispatch: schedule: - cron: "0 */3 * * *" jobs: update: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: token: ${{ secrets.UPDATER }} fetch-depth: 0 - uses: actions/setup-python@v5 with: python-version: "3.x" - name: Install dependencies run: | pip install -r ./ci/envs/requirements-updater.txt - name: Auto Checking Update env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | python ./ci/src/updater.py ${{ secrets.DISCORD_WEBHOOK }} - name: Merge Manifest run: | python ./ci/src/merge-manifest.py - name: Commit & Push changes uses: stefanzweifel/git-auto-commit-action@v4 with: commit_message: "auto updated plugins" push_options: --force branch: main ``` -------------------------------- ### Detect Single Plugin Update from GitHub Source: https://context7.com/flow-launcher/flow.launcher.pluginsmanifest/llms.txt This Python script demonstrates how to check for updates for a single Flow Launcher plugin using the GitHub API. It leverages ETag caching for efficiency and can extract plugin metadata from a zip file. It's designed to work with `aiohttp` for asynchronous HTTP requests. The function `batch_github_plugin_info` handles the API interaction, while `get_plugin_dot_json` extracts metadata from downloaded zip files. ```python import aiohttp from updater import batch_github_plugin_info, get_plugin_dot_json async def check_single_plugin_update(): plugin_info = { "ID": "85eb90ff-b92d-476b-bae4-3d71398e61f9", "Name": "7TV Emotes", "Version": "0.0.3", "UrlSourceCode": "https://github.com/WaterBoiledPizza/7TV-Emotes", "UrlDownload": "https://github.com/WaterBoiledPizza/7TV-Emotes/releases/download/v0.0.3/Flow.Launcher.Plugin.7tvEmotesv0.0.3.zip" } etags = {"85eb90ff-b92d-476b-bae4-3d71398e61f9": "\"abc123\""} github_token = "ghp_your_token_here" webhook_url = "https://discord.com/api/webhooks/" # Check for updates with ETag caching updated_plugin = await batch_github_plugin_info( plugin_info, etags, github_token, webhook_url ) if updated_plugin['Version'] != plugin_info['Version']: print(f"Update detected: {updated_plugin['Name']} v{updated_plugin['Version']}") print(f"New download URL: {updated_plugin['UrlDownload']}") else: print("No updates available (304 Not Modified)") # Download and extract plugin.json from zip async def extract_plugin_metadata(): async with aiohttp.ClientSession() as session: metadata = await get_plugin_dot_json( "https://github.com/WaterBoiledPizza/7TV-Emotes/releases/download/v0.0.3/Flow.Launcher.Plugin.7tvEmotesv0.0.3.zip", session=session ) print(f"Plugin metadata: {metadata}") # Returns: {"ID": "...", "Name": "...", "Description": "...", ...} ``` -------------------------------- ### Validate Flow Launcher Plugin Manifests with Pytest Source: https://context7.com/flow-launcher/flow.launcher.pluginsmanifest/llms.txt The `validator.py` module provides a suite of pytest tests to validate Flow Launcher plugin manifest files. These tests ensure essential criteria such as unique UUIDs, valid URLs, correct file naming conventions, and proper language specifications are met. The `validate_all_plugins` function orchestrates these checks and reports success or failure. ```python from validator import ( test_uuid_unique, test_language_in_list, test_valid_icon_url, test_file_name_construct, test_valid_download_url ) import pytest # Run all validation tests def validate_all_plugins(): try: # Ensure all plugin IDs are unique UUIDs test_uuid_unique() print("✓ All plugin IDs are unique") # Verify languages are in allowed list test_language_in_list() print("✓ All languages are valid") # Check icon URLs are accessible test_valid_icon_url() print("✓ All icon URLs are valid") # Verify filename format: {Name}-{ID}.json test_file_name_construct() print("✓ All filenames match convention") # Validate GitHub download URL format test_valid_download_url() print("✓ All download URLs are valid") print("\nAll validation tests passed!") except AssertionError as e: print(f"✗ Validation failed: {e}") return False return True # Run validation # validate_all_plugins() # Allowed language values # ["csharp", "executable", "fsharp", "python", "javascript", "typescript", # "python_v2", "executable_v2", "javascript_v2", "typescript_v2"] ``` -------------------------------- ### Discord Webhook Notifications for Plugin Updates Source: https://context7.com/flow-launcher/flow.launcher.pluginsmanifest/llms.txt Utilizes the `discord.py` module to send rich embed notifications to Discord webhooks. These notifications alert channels about plugin updates or new releases, including details like release notes, author information, and plugin metadata. Requires `asyncio` for asynchronous operations. ```python import asyncio from discord import update_hook, release_hook async def notify_plugin_update(): webhook_url = "https://discord.com/api/webhooks/123456/abcdef" plugin_info = { "ID": "85eb90ff-b92d-476b-bae4-3d71398e61f9", "Name": "7TV Emotes", "Description": "Search emotes from 7TV", "Author": "Water Boiled Pizza", "Version": "0.0.4", "Language": "python", "Website": "https://github.com/WaterBoiledPizza/7TV-Emotes", "UrlSourceCode": "https://github.com/WaterBoiledPizza/7TV-Emotes", "IcoPath": "https://cdn.jsdelivr.net/gh/WaterBoiledPizza/7TV-Emotes@main/icon.png" } release_data = { "html_url": "https://github.com/WaterBoiledPizza/7TV-Emotes/releases/tag/v0.0.4", "body": "## What's New\n- Added search caching\n- Fixed icon display bug\n- Improved performance" } # Send update notification await update_hook(webhook_url, plugin_info, "0.0.4", release_data) print("Update notification sent to Discord") async def notify_new_plugin(): webhook_url = "https://discord.com/api/webhooks/123456/abcdef" new_plugin = { "ID": "new-uuid-here", "Name": "New Plugin", "Description": "A brand new plugin", "Author": "Developer Name", "Version": "1.0.0", "Language": "csharp", "Website": "https://github.com/user/plugin", "UrlSourceCode": "https://github.com/user/plugin", "IcoPath": "https://cdn.jsdelivr.net/gh/user/plugin@main/icon.png" } # Send new plugin announcement await release_hook(webhook_url, new_plugin) print("New plugin announcement sent to Discord") # asyncio.run(notify_plugin_update()) # asyncio.run(notify_new_plugin()) ``` -------------------------------- ### Plugin Manifest JSON Structure Source: https://github.com/flow-launcher/flow.launcher.pluginsmanifest/blob/main/README.md Defines the essential fields required for a plugin's manifest file. This JSON structure is used by Flow Launcher to register and display plugins in its store. Ensure all fields are accurately populated, especially 'ID' and 'Name'. ```json { "ID": "Unique GUID from your plugin.json, e.g. 2f4e384e-76ce-45c3-aea2-b16f5e5c328f", "Name": "Plugin name, e.g. Hello World Python", "Description": "Short description, e.g. Python Hello World example plugin", "Author": "Author, e.g. Flow Launcher", "Version": "Version from your plugin.json, e.g. 1.0.0", "Language": "Programming language, e.g. python", "Website": "Plugin website, e.g. https://github.com/Flow-Launcher/Flow.Launcher.Plugin.HelloWorldPython", "UrlDownload": "URL to download, e.g. https://github.com/Flow-Launcher/Flow.Launcher.Plugin.HelloWorldPython/releases/download/v1.0.0/Flow.Launcher.Plugin.HelloWorldPython.zip", "UrlSourceCode": "URL to source code, e.g. https://github.com/Flow-Launcher/Flow.Launcher.Plugin.HelloWorldPython/tree/main", "IcoPath": "Plugin icon image's CDN URL, e.g. https://cdn.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.Plugin.HelloWorldPython@main/Images/app.png" } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.