Skip to content

Commit bc7124a

Browse files
authored
Merge branch 'main' into orchestrator
2 parents 9b2d675 + c8fd863 commit bc7124a

18 files changed

Lines changed: 1919 additions & 41 deletions

File tree

.github/workflows/pr-build-and-check.yml

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,128 @@ jobs:
3232

3333
- name: Run Tests
3434
run: yarn test
35+
36+
docs-check:
37+
name: Documentation Validation
38+
runs-on: ubuntu-latest
39+
defaults:
40+
run:
41+
working-directory: docs
42+
steps:
43+
- name: Checkout Repo
44+
uses: actions/checkout@v4
45+
with:
46+
fetch-depth: 0
47+
48+
- name: Check for docs changes
49+
id: docs-changes
50+
working-directory: .
51+
run: |
52+
DOCS_CHANGED=$(git diff --name-only origin/main...HEAD -- docs/ | grep '\.md$' || true)
53+
if [ -z "$DOCS_CHANGED" ]; then
54+
echo "skip=true" >> "$GITHUB_OUTPUT"
55+
echo "No markdown changes in docs/"
56+
else
57+
echo "skip=false" >> "$GITHUB_OUTPUT"
58+
DELIM=$(openssl rand -hex 8)
59+
echo "changed_files<<$DELIM" >> "$GITHUB_OUTPUT"
60+
echo "$DOCS_CHANGED" >> "$GITHUB_OUTPUT"
61+
echo "$DELIM" >> "$GITHUB_OUTPUT"
62+
echo "Changed docs files:"
63+
echo "$DOCS_CHANGED"
64+
fi
65+
66+
- name: Setup Node.js
67+
if: steps.docs-changes.outputs.skip != 'true'
68+
uses: actions/setup-node@v4
69+
with:
70+
node-version: "22"
71+
72+
- name: Enable Corepack
73+
if: steps.docs-changes.outputs.skip != 'true'
74+
run: corepack enable
75+
76+
- name: Install docs dependencies
77+
if: steps.docs-changes.outputs.skip != 'true'
78+
run: yarn install --immutable
79+
80+
- name: Build documentation (validates internal links)
81+
if: steps.docs-changes.outputs.skip != 'true'
82+
run: yarn build
83+
84+
- name: Check external links in changed files
85+
if: steps.docs-changes.outputs.skip != 'true'
86+
run: |
87+
FILES=$(echo "${{ steps.docs-changes.outputs.changed_files }}" | sed 's|^docs/||' | xargs)
88+
echo "Checking files: $FILES"
89+
yarn markdown-link-check --config .markdown-link-check.json $FILES
90+
91+
version-check:
92+
name: Version Bump Check
93+
runs-on: ubuntu-latest
94+
steps:
95+
- name: Checkout Repo
96+
uses: actions/checkout@v4
97+
with:
98+
fetch-depth: 0
99+
100+
- name: Check for source changes requiring version bump
101+
id: changes
102+
run: |
103+
CHANGED_FILES=$(git diff --name-only origin/main...HEAD)
104+
echo "Changed files:"
105+
echo "$CHANGED_FILES"
106+
107+
NEEDS_BUMP=false
108+
while IFS= read -r file; do
109+
case "$file" in
110+
src/*|tsconfig.build.json|tsconfig.base.json)
111+
NEEDS_BUMP=true
112+
break
113+
;;
114+
package.json)
115+
# Check if anything other than "version" changed
116+
OTHER_CHANGES=$(git diff origin/main...HEAD -- package.json | grep -E '^[+-]\s' | grep -v -E '^\+\+\+|^---' | grep -v '"version"' || true)
117+
if [ -n "$OTHER_CHANGES" ]; then
118+
NEEDS_BUMP=true
119+
break
120+
fi
121+
;;
122+
esac
123+
done <<< "$CHANGED_FILES"
124+
125+
echo "needs_bump=$NEEDS_BUMP" >> "$GITHUB_OUTPUT"
126+
127+
- name: Verify version bump and changelog
128+
if: steps.changes.outputs.needs_bump == 'true'
129+
run: |
130+
PR_VERSION=$(node -p "require('./package.json').version")
131+
MAIN_VERSION=$(git show origin/main:package.json | node -p "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).version")
132+
133+
echo "Main version: $MAIN_VERSION"
134+
echo "PR version: $PR_VERSION"
135+
136+
if [ "$PR_VERSION" = "$MAIN_VERSION" ]; then
137+
echo "::error::Source files or dependencies changed but package version was not bumped. Please update the version in package.json."
138+
exit 1
139+
fi
140+
141+
HIGHER=$(node -e "
142+
const [a, b] = ['$PR_VERSION', '$MAIN_VERSION'].map(v => v.split('.').map(Number));
143+
const isHigher = a[0] > b[0] || (a[0] === b[0] && a[1] > b[1]) || (a[0] === b[0] && a[1] === b[1] && a[2] > b[2]);
144+
process.exit(isHigher ? 0 : 1);
145+
" && echo "true" || echo "false")
146+
147+
if [ "$HIGHER" != "true" ]; then
148+
echo "::error::PR version ($PR_VERSION) must be higher than main version ($MAIN_VERSION)."
149+
exit 1
150+
fi
151+
152+
echo "Version bumped from $MAIN_VERSION to $PR_VERSION"
153+
154+
CHANGELOG_CHANGED=$(git diff --name-only origin/main...HEAD -- docs/changelog.md)
155+
if [ -z "$CHANGELOG_CHANGED" ]; then
156+
echo "::error::Version was bumped but docs/changelog.md was not updated. Please add a changelog entry for version $PR_VERSION."
157+
exit 1
158+
fi
159+
echo "Changelog updated for version bump"

.github/workflows/publish-npm.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,44 @@
11
name: Publish package to NPM
22

33
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- "package.json"
49
workflow_dispatch:
510

611
jobs:
12+
check-version:
13+
name: Check Version
14+
runs-on: ubuntu-latest
15+
outputs:
16+
should_publish: ${{ steps.version-check.outputs.should_publish }}
17+
steps:
18+
- name: Checkout Repo
19+
uses: actions/checkout@v4
20+
21+
- name: Check if version changed
22+
id: version-check
23+
run: |
24+
CURRENT_VERSION=$(node -p "require('./package.json').version")
25+
PUBLISHED_VERSION=$(npm view @red-hat-developer-hub/e2e-test-utils version 2>/dev/null || echo "0.0.0")
26+
27+
echo "Current version: $CURRENT_VERSION"
28+
echo "Published version: $PUBLISHED_VERSION"
29+
30+
if [ "$CURRENT_VERSION" = "$PUBLISHED_VERSION" ]; then
31+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
32+
echo "Version $CURRENT_VERSION is already published. Skipping."
33+
else
34+
echo "should_publish=true" >> "$GITHUB_OUTPUT"
35+
echo "New version $CURRENT_VERSION will be published."
36+
fi
37+
738
publish:
839
name: Publish to NPM
40+
needs: check-version
41+
if: needs.check-version.outputs.should_publish == 'true'
942
runs-on: ubuntu-latest
1043
permissions:
1144
contents: read

.husky/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
yarn lint-staged

.lintstagedrc.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"src/**/*.ts": ["eslint", "prettier --check"],
3+
"{*.{js,ts,json,yml,yaml},.github/**/*.yml}": "prettier --check --ignore-unknown",
4+
"docs/**/*.md": "yarn markdown-link-check --config docs/.markdown-link-check.json"
5+
}

docs/.markdown-link-check.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"ignorePatterns": [
3+
{ "pattern": "^<!--@include:" },
4+
{ "pattern": "^/" },
5+
{ "pattern": "^\\.{1,2}/" },
6+
{ "pattern": "^#" }
7+
],
8+
"httpHeaders": [
9+
{
10+
"urls": ["https://github.com"],
11+
"headers": { "Accept": "text/html" }
12+
}
13+
],
14+
"timeout": "10s",
15+
"retryOn429": true,
16+
"retryCount": 3,
17+
"fallbackRetryDelay": "5s",
18+
"aliveStatusCodes": [200, 206, 301, 302]
19+
}

docs/api/deployment/keycloak-helper.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import { KeycloakHelper } from "@red-hat-developer-hub/e2e-test-utils/keycloak";
1414
new KeycloakHelper(options?: KeycloakDeploymentOptions)
1515
```
1616

17-
| Parameter | Type | Description |
18-
|-----------|------|-------------|
17+
| Parameter | Type | Description |
18+
| --------- | --------------------------- | --------------------------------- |
1919
| `options` | `KeycloakDeploymentOptions` | Optional deployment configuration |
2020

2121
## Properties
@@ -102,6 +102,22 @@ async connect(config: KeycloakConnectionConfig): Promise<void>
102102

103103
Connect to an existing Keycloak instance.
104104

105+
### `createUsersAndGroups()`
106+
107+
```typescript
108+
async createUsersAndGroups(realm: string, options?: { users?: KeycloakUserConfig[]; groups?: KeycloakGroupConfig[]; }): Promise<void>
109+
```
110+
111+
Create new users and groups in a realm.
112+
113+
### `deleteUsersAndGroups()`
114+
115+
```typescript
116+
async deleteUsersAndGroups(realm: string, options?: { users?: Array<KeycloakUserConfig | string>; groups?: Array<KeycloakGroupConfig | string> }): Promise<void>
117+
```
118+
119+
Delete users and groups from a realm.
120+
105121
### `createRealm()`
106122

107123
```typescript
@@ -175,6 +191,10 @@ async deleteUser(realm: string, username: string): Promise<void>
175191

176192
Delete a user.
177193

194+
::: warning
195+
Deleting default Keycloak users (see DEFAULT_USERS in [constants](https://github.com/redhat-developer/rhdh-e2e-test-utils/blob/main/src/deployment/keycloak/constants.ts), e.g. `test1`, `test2`) is **not permitted** and will throw an error.
196+
:::
197+
178198
### `deleteGroup()`
179199

180200
```typescript
@@ -183,6 +203,10 @@ async deleteGroup(realm: string, groupName: string): Promise<void>
183203

184204
Delete a group.
185205

206+
::: warning
207+
Deleting default Keycloak groups (see `DEFAULT_GROUPS` in [constants](https://github.com/redhat-developer/rhdh-e2e-test-utils/blob/main/src/deployment/keycloak/constants.ts), e.g. `developers`, `admins`, `viewers`) is **not permitted** and will throw an error.
208+
:::
209+
186210
### `deleteRealm()`
187211

188212
```typescript

docs/changelog.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@
22

33
All notable changes to this project will be documented in this file.
44

5-
## [1.1.17] - Current
5+
## [1.1.18] - Current
6+
7+
### Added
8+
9+
- **KeycloakHelper.createUsersAndGroups(realm: string, options?: { users?: KeycloakUserConfig[]; groups?: KeycloakGroupConfig[];})**: Create users and groups in a realm.
10+
- **KeycloakHelper.deleteUsersAndGroups(realm: string, options?: { users?: Array<KeycloakUserConfig | string>; groups?: Array<KeycloakGroupConfig | string>;})**: Delete users and groups in a realm by their usernames / names or by their KeycloakConfigs. Intended for user and group cleanup in bulk.
11+
12+
### Fixed
13+
14+
- **KeycloakHelper.deleteUser** and **KeycloakHelper.deleteGroup**: Default Keycloak users/groups (see `DEFAULT_USERS` / `DEFAULT_GROUPS`) can no longer be deleted; attempting to delete them throws an error.
15+
16+
## [1.1.17]
617

718
### Added
819

@@ -14,7 +25,7 @@ All notable changes to this project will be documented in this file.
1425

1526
- **Duplicate plugin when no user `dynamic-plugins.yaml` (Keycloak auth, PR build)**: When the workspace had no `dynamic-plugins.yaml`, auto-generated config (with OCI URL) was merged with auth config (with local path). Because merge used exact `package` string match, the same plugin appeared twice and the backend failed with `ExtensionPoint with ID 'keycloak.transformer' is already registered`. The merge now uses a normalized plugin key so OCI and local path for the same logical plugin are deduplicated; the metadata-derived entry (e.g. OCI URL) wins.
1627

17-
## [1.1.15] - Current
28+
## [1.1.15]
1829

1930
### Added
2031

@@ -228,7 +239,7 @@ All notable changes to this project will be documented in this file.
228239
1. **Update imports** - No changes required
229240
2. **Configure authentication** - Use the new `auth` option:
230241
```typescript
231-
await rhdh.configure({ auth: 'keycloak' });
242+
await rhdh.configure({ auth: "keycloak" });
232243
```
233244
3. **Keycloak auto-deployment** - Keycloak is now automatically deployed unless `SKIP_KEYCLOAK_DEPLOYMENT=true`
234245

@@ -245,6 +256,6 @@ After (1.1.x):
245256

246257
```typescript
247258
// Keycloak is auto-deployed and configured
248-
await rhdh.configure({ auth: 'keycloak' });
259+
await rhdh.configure({ auth: "keycloak" });
249260
await rhdh.deploy();
250261
```

docs/guide/deployment/authentication.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,18 @@ No additional environment variables required.
152152

153153
### Keycloak Auth
154154

155-
These are automatically set by `KeycloakHelper.configureForRHDH()`:
156-
157-
| Variable | Description |
158-
|----------|-------------|
159-
| `KEYCLOAK_BASE_URL` | Keycloak instance URL |
160-
| `KEYCLOAK_REALM` | Realm name |
161-
| `KEYCLOAK_CLIENT_ID` | OIDC client ID |
162-
| `KEYCLOAK_CLIENT_SECRET` | OIDC client secret |
163-
| `KEYCLOAK_METADATA_URL` | OIDC discovery URL |
164-
| `KEYCLOAK_LOGIN_REALM` | Login realm name |
155+
These are automatically set by `KeycloakHelper.configureForRHDH()` or populated from global workspace in the vault:
156+
157+
| Variable | Description |
158+
| ------------------------------- | --------------------- |
159+
| `KEYCLOAK_BASE_URL` | Keycloak instance URL |
160+
| `KEYCLOAK_REALM` | Realm name |
161+
| `KEYCLOAK_CLIENT_ID` | OIDC client ID |
162+
| `KEYCLOAK_CLIENT_SECRET` | OIDC client secret |
163+
| `KEYCLOAK_METADATA_URL` | OIDC discovery URL |
164+
| `KEYCLOAK_LOGIN_REALM` | Login realm name |
165+
| `VAULT_KEYCLOAK_ADMIN_USERNAME` | Admin username |
166+
| `VAULT_KEYCLOAK_ADMIN_PASSWORD` | Admin password |
165167

166168
### GitHub Auth
167169

docs/guide/deployment/keycloak-deployment.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ await keycloak.configureForRHDH({
9292
clientId: "my-client",
9393
clientSecret: "my-secret",
9494
},
95-
groups: ["developers", "admins"],
95+
groups: [{ name: "developers" }, { name: "admins" }],
9696
users: [
9797
{ username: "user1", password: "pass1", groups: ["developers"] },
9898
{ username: "user2", password: "pass2", groups: ["admins"] },
@@ -156,6 +156,18 @@ const users = await keycloak.getUsers("rhdh");
156156

157157
// Delete user
158158
await keycloak.deleteUser("rhdh", "username");
159+
160+
// Create multiple users
161+
await keycloak.createUsersAndGroups("rhdh", {
162+
users: [
163+
{ username: "user1", password: "pass1" },
164+
{ username: "user2", password: "pass2", groups: ["developers"] },
165+
],
166+
});
167+
168+
// Delete multiple users - by objects or by usernames
169+
await keycloak.deleteUsersAndGroups("rhdh", { users });
170+
await keycloak.deleteUsersAndGroups("rhdh", { users: ["user1", "user2"] });
159171
```
160172

161173
### Group Management
@@ -169,6 +181,15 @@ const groups = await keycloak.getGroups("rhdh");
169181

170182
// Delete group
171183
await keycloak.deleteGroup("rhdh", "testers");
184+
185+
// Create multiple groups
186+
await keycloak.createUsersAndGroups("rhdh", {
187+
groups: [{ name: "admins" }, { name: "viewers" }],
188+
});
189+
190+
// Delete multiple groups - by objects or by group names
191+
await keycloak.deleteUsersAndGroups("rhdh", { groups });
192+
await keycloak.deleteUsersAndGroups("rhdh", { groups: ["admins", "viewers"] });
172193
```
173194

174195
### Using getUsers and getGroupsOfUser in tests

docs/overlay/reference/environment-variables.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,10 @@ test.beforeAll(async ({ rhdh }) => {
215215
### Validating Required Variables
216216

217217
```typescript
218+
import { requireEnv } from "@red-hat-developer-hub/e2e-test-utils/utils";
219+
218220
test.beforeAll(async ({ rhdh }) => {
219-
const requiredVars = ["VAULT_API_KEY", "VAULT_SECRET"];
220-
for (const varName of requiredVars) {
221-
if (!process.env[varName]) {
222-
throw new Error(`Required variable ${varName} is not set`);
223-
}
224-
}
221+
requireEnv("VAULT_API_KEY", "VAULT_SECRET");
225222

226223
await rhdh.configure({ auth: "keycloak" });
227224
await rhdh.deploy();

0 commit comments

Comments
 (0)