Skip to content

Commit 64db026

Browse files
authored
Add support for content tags (#70)
* Add tag support to extension list * Add tag checking to linting automation * Add tags section to CONTRIBUTING.md
1 parent 9ccb8ed commit 64db026

File tree

5 files changed

+75
-9
lines changed

5 files changed

+75
-9
lines changed

.github/actions/lint-extension/action.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,29 @@ runs:
3535
' ./extensions/${{ inputs.extension-name }}/manifest.json
3636
shell: bash
3737
38+
# Ensures that the manifest.json has only valid tags
39+
- name: Check tags
40+
run: |
41+
# Read in valid tags from extensions.json
42+
VALID_TAGS=$(jq -c '.tags' ./extensions.json)
43+
44+
# Read in tags from the extension's manifest.json
45+
EXTENSION_TAGS=$(jq -c '.extension.tags // []' ./extensions/${{ inputs.extension-name }}/manifest.json)
46+
47+
# Check if any extension tag is not in the valid tags list
48+
INVALID_TAGS=$(jq -n -c --argjson global "$VALID_TAGS" --argjson extension "$EXTENSION_TAGS" '
49+
$extension | map(. as $tag | if ($global | index($tag) | not) then $tag else empty end)
50+
')
51+
52+
# Check if there are any invalid tags
53+
if [ "$INVALID_TAGS" != "[]" ]; then
54+
echo "Error: The following tags in manifest.json are not defined in extensions.json:"
55+
echo "$INVALID_TAGS"
56+
echo "Please add these tags to the 'tags' array in extensions.json or remove them from the manifest."
57+
exit 1
58+
fi
59+
shell: bash
60+
3861
- uses: actions/setup-node@v4
3962

4063
- run: npm install -g semver

CONTRIBUTING.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ to the `manifest.json`:
4040
"title": "My Content Name",
4141
"description": "A lovely, detailed description of the content.",
4242
"homepage": "https://github.com/posit-dev/connect-extensions/tree/main/extensions/my-content-name",
43+
"tags": [],
4344
"version": "0.0.0"
4445
}
4546
}
@@ -49,6 +50,36 @@ It is recommended to begin with `"version": "0.0.0"` to avoid triggering a
4950
release during development of your content. When you are ready to release to
5051
the gallery check the [Adding content to the Connect Gallery](#adding-content-to-the-connect-gallery) section.
5152

53+
#### Tags
54+
55+
The `tags` array in the `extension` section of the `manifest.json` is optional,
56+
but it helps users filter content in the Gallery. A good start is to include the
57+
languages and tools used:
58+
59+
```json
60+
// manifest.json
61+
62+
{
63+
...
64+
"extension": {
65+
"tags": ["python", "quarto"],
66+
...
67+
}
68+
}
69+
```
70+
71+
Available tags are listed in the [`extensions.json`](./extensions.json) and the
72+
automations in the repository will check that any included tags are valid.
73+
74+
##### Adding a new tag to the gallery
75+
76+
If you want to include a tag on content that is not already in the
77+
[`extensions.json`](./extensions.json) file, it can be added.
78+
79+
Pull requests can add new tags, but ensure that the tag follows the patterns
80+
of other tags and is not a duplicate. New tags should be added sparingly and
81+
reviewed carefully.
82+
5283
### README.md
5384

5485
In the above extension section there is a `homepage` link that we provide in the

extensions.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"tags": [],
23
"extensions": [
34
{
45
"name": "portfolio-dashboard",

scripts/extension-list.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,18 @@ function sortExtensionVersions(extension: Extension) {
3131
}
3232

3333
class ExtensionList {
34-
constructor(public extensions: Extension[]) {
34+
constructor(public tags: string[], public extensions: Extension[]) {
35+
this.tags = tags;
3536
this.extensions = extensions;
3637
}
3738

3839
static fromFile(path: string) {
39-
const extensions = JSON.parse(fs.readFileSync(path, "utf8")).extensions;
40-
return new ExtensionList(extensions);
40+
const file = JSON.parse(fs.readFileSync(path, "utf8"));
41+
return new ExtensionList(file.tags, file.extensions);
4142
}
4243

4344
public addRelease(manifest: ExtensionManifest, githubRelease) {
44-
const { name, title, description, homepage, version } = manifest.extension;
45+
const { name, title, description, homepage, version, tags } = manifest.extension;
4546
const { assets, published_at } = githubRelease;
4647

4748
const { browser_download_url } = assets.find(
@@ -55,10 +56,10 @@ class ExtensionList {
5556
};
5657

5758
if (this.getExtension(name)) {
58-
this.updateExtensionDetails(name, title, description, homepage);
59+
this.updateExtensionDetails(name, title, description, homepage, tags);
5960
this.addExtensionVersion(name, newVersion);
6061
} else {
61-
this.addNewExtension(name, title, description, homepage, newVersion);
62+
this.addNewExtension(name, title, description, homepage, newVersion, tags);
6263
}
6364
}
6465

@@ -70,13 +71,15 @@ class ExtensionList {
7071
name: string,
7172
title: string,
7273
description: string,
73-
homepage: string
74+
homepage: string,
75+
tags: string[] = []
7476
) {
7577
this.updateExtension(name, {
7678
...this.getExtension(name),
7779
title,
7880
description,
7981
homepage,
82+
tags,
8083
});
8184
}
8285

@@ -109,7 +112,8 @@ class ExtensionList {
109112
title: string,
110113
description: string,
111114
homepage: string,
112-
initialVersion: ExtensionVersion
115+
initialVersion: ExtensionVersion,
116+
tags: string[] = []
113117
) {
114118
if (this.getExtension(name) !== undefined) {
115119
throw new Error(`Extension ${name} already exists in the list`);
@@ -121,6 +125,7 @@ class ExtensionList {
121125
homepage,
122126
latestVersion: initialVersion,
123127
versions: [initialVersion],
128+
tags,
124129
});
125130
this.sortExtensions();
126131
}
@@ -134,7 +139,11 @@ class ExtensionList {
134139
}
135140

136141
public stringify() {
137-
return JSON.stringify({ extensions: this.extensions }, null, 2);
142+
const output = {
143+
tags: this.tags,
144+
extensions: this.extensions
145+
}
146+
return JSON.stringify(output, null, 2);
138147
}
139148

140149
private sortExtensions() {

scripts/types/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface ExtensionManifest {
55
description: string;
66
homepage: string;
77
version: string;
8+
tags?: string[];
89
};
910
}
1011

@@ -21,4 +22,5 @@ export interface Extension {
2122
homepage: string;
2223
latestVersion: ExtensionVersion;
2324
versions: ExtensionVersion[];
25+
tags: string[];
2426
}

0 commit comments

Comments
 (0)