Skip to content

Commit 336092c

Browse files
authored
Merge branch 'main' into main
2 parents 34ceba5 + e53d805 commit 336092c

81 files changed

Lines changed: 4399 additions & 3823 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/pr_assignee.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Assign PR to creator
2+
3+
on:
4+
pull_request_target:
5+
types: [opened]
6+
branches-ignore:
7+
- l10n_dev
8+
9+
permissions:
10+
pull-requests: write
11+
12+
jobs:
13+
automation:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Assign PR to creator
17+
uses: toshimaru/auto-author-assign@v3.0.1

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,23 @@ Looking for a list of currently available plugins in Flow? Visit [here](https://
3535
"IcoPath": "Plugin icon image's CDN URL, e.g. https://cdn.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.Plugin.HelloWorldPython@main/Images/app.png"
3636
}
3737
```
38-
5. Optionally, a plugin can also have the "MinimumAppVersion" property set, restricting it to only appear and be installable on versions of Flow Launcher that meet or exceed the specified version number. For example, setting `"MinimumAppVersion": "2.0.0"` ensures the plugin will not show up or be installable on any Flow Launcher version older than 2.0.0.
38+
5. Optionally, a plugin can also have the `MinimumAppVersion` property set, restricting it to only appear and be installable on versions of Flow Launcher that meet or exceed the specified version number. For example, setting `"MinimumAppVersion": "2.0.0"` ensures the plugin will not show up or be installable on any Flow Launcher version older than 2.0.0.
3939
6. For `IcoPath`, use a CDN provider for global accessibility. [jsdelivr.com](https://www.jsdelivr.com/) for example as shown above, works well with GitHub repositories.
4040
7. It is a requirement to set up a GitHub Actions workflow for automated build and release. Follow the guide [here](https://www.flowlauncher.com/docs/#/py-setup-project?id=_1-add-github-workflow) and use [this](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.HelloWorldPython/blob/main/.github/workflows/Publish%20Release.yml) as an example.
4141
8. It is a requirement that your plugin conforms with the [Plugin Store policy](#plugin-store-policy).
42-
9. Submit a pull request.
42+
9. Submit a pull request targeting the default branch.
4343
10. The plugin will be available in Flow after the pull request is approved by the Flow Launcher Team.
4444

4545
Flow downloads the manifest (plugins.json) file from various CDN providers, which means the availability of your new plugin depends on when these providers sync their updated files. This syncing process can take several days and sometimes up to a week across all providers. During this period, you may see intermittent updates for your plugin in the manifest, as the provider chosen for retrieval is selected randomly based on the fastest fetch speed.
4646

4747
While the plugin has not yet appeared in the store, you and your users can manually install it using `pm install <url/local path>`.
4848

49+
## How to remove your plugin
50+
51+
1. Remove the `${name}-${uuid}.json` file for your plugin from the [plugins](https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest/tree/main/plugins) directory.
52+
2. Submit a pull request targeting the default branch.
53+
54+
Note that the published `plugins.json` manifest is regenerated by CI on a schedule (approximately every three hours) and then distributed via multiple CDN providers. As a result, your removed plugin may continue to appear in the plugin store or manifest for some time after your pull request has been merged, until all providers have refreshed their caches.
4955
## Plugin updates
5056

5157
Every three hours the *CI* in this repository will check for new updates from plugins and automatically update them to the latest version.

ci/src/_utils.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,31 @@
4646
description = "Description"
4747
plugin_name = "Name"
4848
github_url = "https://github.com"
49+
tested = "Tested"
4950
release_date = "LatestReleaseDate"
5051
date_added = "DateAdded"
5152
website = "Website"
53+
minimum_app_version = "MinimumAppVersion"
54+
55+
necessary_fields = (
56+
id_name,
57+
plugin_name,
58+
description,
59+
author,
60+
version,
61+
language_name,
62+
website,
63+
url_download,
64+
url_sourcecode,
65+
icon_path,
66+
)
67+
68+
optional_fields = (
69+
minimum_app_version,
70+
tested,
71+
release_date,
72+
date_added
73+
)
5274

5375
# typing
5476
PluginType = Dict[str, str]
@@ -59,6 +81,22 @@
5981
ETagsType = Dict[str, str]
6082

6183

84+
def _raise_on_duplicate_keys(pairs):
85+
seen = set()
86+
duplicates = set()
87+
result = {}
88+
for key, value in pairs:
89+
if key in seen:
90+
duplicates.add(key)
91+
else:
92+
seen.add(key)
93+
result[key] = value
94+
if duplicates:
95+
sorted_duplicates = sorted(duplicates)
96+
raise ValueError(f"Duplicate keys found: {sorted_duplicates}")
97+
return result
98+
99+
62100
def plugin_reader() -> P:
63101
plugin_file_paths = get_plugin_file_paths()
64102

ci/src/validator.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# -*-coding: utf-8 -*-
2+
import json
23
import uuid
34

45
from _utils import (check_url, clean, get_new_plugin_submission_ids, get_plugin_file_paths, get_plugin_filenames,
5-
icon_path, id_name, language_list, language_name, plugin_reader, github_download_url_regex, url_download)
6+
icon_path, id_name, language_list, language_name, plugin_reader, github_download_url_regex,
7+
url_download, necessary_fields, optional_fields, _raise_on_duplicate_keys, plugin_name)
68

79
plugin_infos = plugin_reader()
810

@@ -39,8 +41,8 @@ def test_file_name_construct():
3941
filenames = get_plugin_filenames()
4042
for info in plugin_infos:
4143
assert (
42-
f"{info['Name']}-{info['ID']}.json" in filenames
43-
), f"Plugin {info['Name']} with ID {info['ID']} does not have the correct filename. Make sure it's name + ID, i.e. {info['Name']}-{info['ID']}.json"
44+
f"{info[plugin_name]}-{info[id_name]}.json" in filenames
45+
), f"Plugin {info[plugin_name]} with ID {info[id_name]} does not have the correct filename. Make sure it's name + ID, i.e. {info[plugin_name]}-{info[id_name]}.json"
4446

4547

4648
def test_submitted_plugin_id_is_valid_uuid():
@@ -53,6 +55,29 @@ def test_submitted_plugin_id_is_valid_uuid():
5355

5456
assert outcome is True, f"The submission plugin ID {id} is not a valid v4 UUID"
5557

58+
5659
def test_valid_download_url():
5760
for info in plugin_infos:
58-
assert github_download_url_regex.fullmatch(info[url_download]), f" The plugin {info['Name']}-{info['ID']} does not have a valid download url: {info[url_download]}"
61+
assert github_download_url_regex.fullmatch(info[url_download]), f"The plugin {info[plugin_name]} with ID {info[id_name]} does not have a valid download url: {info[url_download]}"
62+
63+
64+
def test_necessary_fields():
65+
for info in plugin_infos:
66+
missing_fields = [field for field in necessary_fields if field not in info]
67+
assert not missing_fields, f"Plugin {info[plugin_name]} with ID {info[id_name]} is missing fields: {missing_fields}"
68+
69+
70+
def test_optional_fields():
71+
allowed_fields = set(necessary_fields) | set(optional_fields)
72+
for info in plugin_infos:
73+
unknown_fields = [field for field in info if field not in allowed_fields]
74+
assert not unknown_fields, f"Plugin {info[plugin_name]} with ID {info[id_name]} has unknown fields: {unknown_fields}"
75+
76+
77+
def test_no_duplicate_fields():
78+
for file_path in get_plugin_file_paths():
79+
with open(file_path, "r", encoding="utf-8") as f:
80+
try:
81+
json.load(f, object_pairs_hook=_raise_on_duplicate_keys)
82+
except ValueError as e:
83+
assert False, f"Plugin file {file_path} has {e}"

0 commit comments

Comments
 (0)