Skip to content

Commit a5522ac

Browse files
authored
Add support for requiredFeatures specification (#84)
* Add requiredFeatures to extension list * Add linting for required features * Add required features section to contributing * Add requiredFeatures to extension-list script * Add required feature to stock-api-fastapi manifest
1 parent f113c50 commit a5522ac

File tree

6 files changed

+82
-3
lines changed

6 files changed

+82
-3
lines changed

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,35 @@ runs:
3636
' ./extensions/${{ inputs.extension-name }}/manifest.json
3737
shell: bash
3838
39+
# Ensures that the manifest.json has only valid requiredFeatures
40+
- name: Check required features
41+
run: |
42+
# Read in valid requiredFeatures from extensions.json
43+
VALID_FEATURES=$(jq -c '.requiredFeatures' ./extensions.json)
44+
45+
# Read in requiredFeatures from the extension's manifest.json
46+
EXTENSION_REQUIRED_FEATURES=$(jq -c '.extension.requiredFeatures // []' ./extensions/${{ inputs.extension-name }}/manifest.json)
47+
48+
# Check if the extension's requiredFeatures is an array
49+
if [ "$(jq -r 'type' <<< "$EXTENSION_REQUIRED_FEATURES")" != "array" ]; then
50+
echo "Error: The requiredFeatures in manifest.json must be an array."
51+
exit 1
52+
fi
53+
54+
# Check if any extension requiredFeatures are not in the valid requiredFeatures list
55+
INVALID_FEATURES=$(jq -n -c --argjson global "$VALID_FEATURES" --argjson extension "$EXTENSION_REQUIRED_FEATURES" '
56+
$extension | map(. as $feature | if ($global | index($feature) | not) then $feature else empty end)
57+
')
58+
59+
# Check if there are any invalid requiredFeatures
60+
if [ "$INVALID_FEATURES" != "[]" ]; then
61+
echo "Error: The following `requiredFeatures` in manifest.json are not defined in extensions.json:"
62+
echo "$INVALID_FEATURES"
63+
echo "Please add these features to the 'requiredFeatures' array in extensions.json or remove them from the manifest."
64+
exit 1
65+
fi
66+
shell: bash
67+
3968
# Ensures that the manifest.json has only valid tags
4069
- name: Check tags
4170
run: |

CONTRIBUTING.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,27 @@ is recorded for that specific release.
9393
A good Connect Version to start with is `"2025.04.0"` since it is the
9494
release that introduced the Connect Gallery.
9595

96+
#### Required Connect Features
97+
98+
The `requiredFeatures` field in the `extension` section of the `manifest.json`
99+
is optional, but if your content requires enhanced features of Posit Connect to
100+
function correctly it should be included.
101+
102+
For example, if your content requires the API Publishing feature your
103+
`manifest.json` should include the following:
104+
105+
```json
106+
// manifest.json
107+
108+
{
109+
...
110+
"extension": {
111+
"requiredFeatures": ["API Publishing"],
112+
...
113+
}
114+
}
115+
```
116+
96117
### README.md
97118

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

extensions.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
{
22
"tags": [],
3+
"requiredFeatures": [
4+
"API Publishing",
5+
"OAuth Integrations",
6+
"Current User Execution"
7+
],
38
"extensions": [
49
{
510
"name": "connectwidgets-example",

extensions/stock-api-fastapi/manifest.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
"homepage": "https://github.com/posit-dev/connect-extensions/tree/main/extensions/stock-api-fastapi",
1313
"tags": [],
1414
"minimumConnectVersion": "2025.04.0",
15+
"requiredFeatures": [
16+
"API Publishing"
17+
],
1518
"version": "1.0.0"
1619
},
1720
"environment": {

scripts/extension-list.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import semverValid from "semver/functions/valid";
55
import semverEq from "semver/functions/eq";
66
import semverRcompare from "semver/functions/rcompare";
77

8-
import { Extension, ExtensionManifest, ExtensionVersion } from "./types";
8+
import {
9+
Extension,
10+
ExtensionManifest,
11+
ExtensionVersion,
12+
RequiredFeature
13+
} from "./types";
914

1015
function getExtensionNameFromRelease(release: any): string {
1116
const tag = release.tag_name as string;
@@ -31,14 +36,19 @@ function sortExtensionVersions(extension: Extension) {
3136
}
3237

3338
class ExtensionList {
34-
constructor(public tags: string[], public extensions: Extension[]) {
39+
constructor(
40+
public tags: string[],
41+
public requiredFeatures: RequiredFeature[],
42+
public extensions: Extension[]
43+
) {
3544
this.tags = tags;
45+
this.requiredFeatures = requiredFeatures;
3646
this.extensions = extensions;
3747
}
3848

3949
static fromFile(path: string) {
4050
const file = JSON.parse(fs.readFileSync(path, "utf8"));
41-
return new ExtensionList(file.tags, file.extensions);
51+
return new ExtensionList(file.tags, file.requiredFeatures, file.extensions);
4252
}
4353

4454
public addRelease(manifest: ExtensionManifest, githubRelease) {
@@ -50,6 +60,7 @@ class ExtensionList {
5060
version,
5161
tags,
5262
minimumConnectVersion,
63+
requiredFeatures,
5364
} = manifest.extension;
5465
const { assets, published_at } = githubRelease;
5566

@@ -62,6 +73,7 @@ class ExtensionList {
6273
released: published_at,
6374
url: browser_download_url,
6475
minimumConnectVersion: minimumConnectVersion,
76+
...(requiredFeatures ? { requiredFeatures } : {}),
6577
};
6678

6779
if (this.getExtension(name)) {
@@ -150,6 +162,7 @@ class ExtensionList {
150162
public stringify() {
151163
const output = {
152164
tags: this.tags,
165+
requiredFeatures: this.requiredFeatures,
153166
extensions: this.extensions
154167
}
155168
return JSON.stringify(output, null, 2);

scripts/types/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,23 @@ export interface ExtensionManifest {
66
homepage: string;
77
version: string;
88
minimumConnectVersion: string;
9+
requiredFeatures?: RequiredFeature[];
910
tags?: string[];
1011
};
1112
}
1213

14+
export enum RequiredFeature {
15+
API_PUBLISHING = 'API Publishing',
16+
OAUTH_INTEGRATIONS = 'OAuth Integrations',
17+
CURRENT_USER_EXECUTION = 'Current User Execution',
18+
}
19+
1320
export interface ExtensionVersion {
1421
version: string;
1522
released: string;
1623
url: string;
1724
minimumConnectVersion: string;
25+
requiredFeatures?: RequiredFeature[];
1826
}
1927

2028
export interface Extension {

0 commit comments

Comments
 (0)