| name | types-update |
| description | Automates the full lifecycle of updating @forklift-ui/types: discovers upstream versions, creates a Jira ticket, runs type generation scripts in the forked types repo, handles KubeVirt/CDI conflicts, creates a PR and GitHub release, waits for npm publish, then bumps the consumer project and monitors CI. Use when the user asks to update types, bump types, update forklift-ui/types, types package update, or regenerate types. |
Types Update Skill
Updates @forklift-ui/types from upstream sources (Forklift, Kubernetes, KubeVirt, CDI), publishes a new version, and bumps the consumer project.
For conflict resolution rules, Jira payload template, CRD verification, and edge cases see reference.md.
Prerequisites
gh CLI authenticated with access to kubev2v/forklift-console-types
- User's fork of
kubev2v/forklift-console-types cloned locally (or clonable)
~/.jira-creds configured (see ~/.cursor/rules/jira-api.mdc)
curl, jq, npm, node >= 20 installed
- Java runtime (required by
openapi-generator-cli in the types repo)
Permissions — Cross-Project File Access
The types repo (forklift-console-types) lives outside the current workspace. The Cursor sandbox blocks writes to paths outside the workspace, which causes repeated permission prompts.
Rules for all phases that touch the types repo (Phases 2.5, 3, 4):
- Always use
required_permissions: ["all"] on every Shell call whose working_directory is the types repo.
- Use Shell for file edits in the types repo — do NOT use the Write or StrReplace tools for files outside the workspace. Instead write files via shell (e.g.
cat <<'CONTENT' > path/to/file.ts ... CONTENT). This avoids per-file permission prompts.
- Batch operations — combine multiple related edits into a single Shell call when possible to minimise approval prompts.
- Phases that only run in the consumer project (this workspace) do not need special permissions.
Workflow
Copy this checklist and track progress:
Types Update Progress:
- [ ] Phase 1: Setup and discovery
- [ ] Phase 2: Create Jira ticket
- [ ] Phase 2.5: Update inventory types from Go source
- [ ] Phase 3: Update generated types in forked repo
- [ ] GATE 1: User approves conflict resolution (if any)
- [ ] Phase 4: Version bump, tracking update, and PR
- [ ] GATE 2: User confirms types PR is merged
- [ ] Phase 5: Create GitHub release and wait for npm publish
- [ ] Phase 6: Update consumer project (forklift-console-plugin)
- [ ] Phase 7: Monitor CI on consumer PR
Phase 1: Setup and Discovery
1a. Locate the types repo
Check if the types repo is already cloned locally. Common locations:
- Sibling directory:
../forklift-console-types
- User home:
~/Workspace/forklift-console-types
If not found, clone the user's fork:
gh repo clone <user>/forklift-console-types ~/Workspace/forklift-console-types
Store the path as TYPES_REPO_DIR for the rest of the workflow.
1b. Read current versions
Parse the Version Tracking table in $TYPES_REPO_DIR/MAINTENANCE.md. Extract the "Current Version" column for each source (Forklift, Kubernetes, KubeVirt, CDI).
Also read the current package version from $TYPES_REPO_DIR/package.json.
1c. Fetch latest upstream versions
Run the helper script:
.cursor/skills/types-update/scripts/check-upstream.sh
This outputs the latest commit SHA on main for Forklift, and the latest release tag for the other sources.
Forklift always targets main (never a tagged release) because the UI needs the most recent CRD and inventory type changes. The commit SHA is recorded in MAINTENANCE.md for reproducibility. Kubernetes, KubeVirt, and CDI use stable tagged releases.
1d. Present comparison and ask user
Present a table:
Types Update Discovery
=======================
Package version: 1.0.8
| Source | Current | Latest Available |
|------------|----------------------|------------------------|
| Forklift | main (abc1234) | main (def5678) |
| Kubernetes | v1.29.0 | v1.30.0 |
| KubeVirt | v1.2.0 | v1.3.0 |
| CDI | v1.58.0 | v1.59.0 |
Use the AskQuestion tool to ask:
- Which sources to update (default: all four). Options: "All four", "Let me pick"
- If "Let me pick", present each source as a multi-select checkbox (Forklift is always included since it targets
main)
Then ask for target versions for Kubernetes, KubeVirt, and CDI (default: the latest available shown above). Forklift always uses the latest main commit.
Phase 2: Create Jira Ticket
Source Jira credentials:
source ~/.jira-creds
Determine the new package version (current patch + 1, e.g. 1.0.8 -> 1.0.9). Ask user if they want a different bump level (minor/major).
Create the ticket:
curl -s -u "${JIRA_EMAIL}:${JIRA_API_TOKEN}" \
-X POST -H "Content-Type: application/json" \
-d '{
"fields": {
"project": {"key": "MTV"},
"issuetype": {"name": "Task"},
"summary": "UI types package bump from <OLD_VERSION> to <NEW_VERSION>",
"description": "Updating @forklift-ui/types package.\n\nSources updated:\n- Forklift: <version> (CRDs + inventory types)\n- Kubernetes: <version>\n- KubeVirt: <version>\n- CDI: <version>",
"components": [{"name": "User Interface"}],
"labels": ["user-interface"]
}
}' \
"${JIRA_BASE_URL}/rest/api/2/issue/"
Parse the response to extract the ticket key (e.g. MTV-5432). Store it as JIRA_KEY for commits and PR descriptions.
If the API call fails (auth error, missing fields), show the error and ask the user to fix credentials or provide the component/label IDs manually. See reference.md for the full payload template and fallback field discovery commands.
Phase 2.5: Update Inventory Types from Go Source
The hand-written inventory types in $TYPES_REPO_DIR/src/types/ mirror Go structs from the Forklift repo. The agent reads the Go source files and regenerates the TypeScript interfaces to match.
Permissions reminder: This phase edits files in the types repo (outside workspace). Use Shell with required_permissions: ["all"] for all commands and file writes. See the Permissions section above.
Scope: src/types/provider/ (per-provider types) and src/types/provider/base/ (shared model types). The src/types/secret/ and src/types/k8s/ directories are NOT derivable from Go and remain manual.
See reference.md for the complete Go source file map, type mapping table, and edge cases.
2.5a. Fetch Go source files
For each provider, download the Go source files from GitHub at the same Forklift version/branch selected in Phase 1. Use the GitHub raw content URL:
https://raw.githubusercontent.com/kubev2v/forklift/<VERSION>/pkg/controller/provider/web/<provider>/<file>.go
https://raw.githubusercontent.com/kubev2v/forklift/<VERSION>/pkg/controller/provider/model/<provider>/model.go
Start with the base model:
curl -sfL "https://raw.githubusercontent.com/kubev2v/forklift/<VERSION>/pkg/controller/provider/model/base/model.go"
Then fetch each provider's web layer files. The Go source directory for each provider:
| TypeScript provider dir | Go web dir | Go model dir |
|---|
provider/base/ | -- | model/base/model.go |
provider/vsphere/ | web/vsphere/*.go | model/vsphere/model.go |
provider/ovirt/ | web/ovirt/*.go | model/ovirt/model.go |
provider/openshift/ | web/ocp/*.go | model/ocp/model.go |
provider/openstack/ | web/openstack/*.go | model/openstack/model.go |
provider/ova/ | web/ova/*.go | -- |
provider/hyperv/ | web/hyperv/*.go | model/hyperv/model.go |
To discover files in a Go web directory, list them via the GitHub API:
curl -sf "https://api.github.com/repos/kubev2v/forklift/contents/pkg/controller/provider/web/<provider>?ref=<VERSION>" | jq -r '.[].name'
2.5b. Parse Go structs and translate to TypeScript
For each Go source file, identify exported struct definitions and their fields. Apply the Go-to-TypeScript type mapping from reference.md.
Key rules:
- Struct field with
json:"fieldName" -> TypeScript property fieldName: <mapped-type>
- Struct field with
json:"-" -> skip (not serialized)
- Struct field with
json:"fieldName,omitempty" -> optional property fieldName?: <mapped-type>
- Pointer field (
*T) -> optional property (T | undefined or ?)
- Embedded struct -> TypeScript
extends (e.g., type VM struct { Base; ... } becomes interface VM extends Base { ... })
- Go
api.Provider (imported from Forklift API package) -> V1beta1Provider from ../../../generated
2.5c. Write the TypeScript files
Overwrite the existing TypeScript files in $TYPES_REPO_DIR/src/types/provider/ with the regenerated interfaces. Preserve:
- File structure: same file names and directory layout as existing
- Source URL comment: include
// https://github.com/kubev2v/forklift/tree/<VERSION>/pkg/controller/provider/web/<provider>/<file>.go at the top
- Go field comments: include the original Go field definition as a comment above each TypeScript property (matching existing style)
- Import paths: maintain correct relative imports to base types and generated CRD types
- Union types: after regenerating individual provider types, regenerate
ProviderInventory.ts, ProviderVM.ts, ProviderHost.ts, and ProvidersInventoryList.ts to include all providers
2.5d. Verify inventory types build
cd $TYPES_REPO_DIR
npm run build
If the build fails due to inventory type issues (missing imports, wrong type references), fix them before proceeding. Common issues:
- Missing base type: a Go struct embeds a type from a different package. Check if the base type exists in
provider/base/model.ts or another provider's types.
- CRD type reference changed: if a Go struct references an API type that was renamed in the generated types, update the import.
- New provider added upstream: if a new provider directory appears in the Go source, create the corresponding TypeScript directory under
provider/ and add it to provider/index.ts and the union types.
Phase 3: Update Generated Types in Forked Repo
Permissions reminder: This phase runs in the types repo (outside workspace). Use Shell with required_permissions: ["all"] for all commands. See the Permissions section above.
3a. Prepare the branch (skip if already done in Phase 2.5)
cd $TYPES_REPO_DIR
git fetch origin
git checkout main
git pull origin main
git checkout -b chore/types-update-<NEW_VERSION>
npm ci --ignore-scripts
3b. Verify Forklift CRD list (if updating Forklift)
Before running the Forklift update script, compare the hardcoded CRD list in scripts/update-forklift.sh against the actual upstream directory. See reference.md for the verification procedure. If new CRDs exist upstream, add them to the CRDS array before running.
3c. Run update scripts
Run each selected source's update script in this order:
npm run update:forklift -- main
npm run update:kubernetes -- <version>
npm run update:kubevirt -- <version>
npm run update:cdi -- <version>
Forklift always uses main. Record the commit SHA from Phase 1 in MAINTENANCE.md for reproducibility.
macOS compatibility note: The update scripts use sed -i which behaves differently on macOS (BSD sed) vs Linux (GNU sed). If running locally on macOS and sed errors occur, set PATH="/opt/homebrew/opt/gnu-sed/libexec/gnubin:$PATH" or install gnu-sed via Homebrew.
3d. Check for KubeVirt/CDI conflicts
After updating KubeVirt or CDI, run:
npm run check:conflicts
Parse the output:
- "No conflicts found": proceed to build verification
- Conflicts found: the script lists conflicting type names
If conflicts are found, update src/generated/index.ts:
- Read the current selective export block for kubevirt
- Remove any newly conflicting types from the named export list
- Add any new KubeVirt-only types that the application needs
See reference.md for the conflict resolution rules and which type categories to include/exclude.
GATE 1: Conflict resolution approval
If conflicts required changes to src/generated/index.ts, present the diff to the user:
git diff src/generated/index.ts
Use AskQuestion:
- "Approve changes" -> proceed
- "Let me review" -> wait for user edits
- "Cancel" -> abort workflow
If no conflicts were found, skip this gate.
3e. Fix ObjectMeta timestamp types
The openapi-generator maps OpenAPI date-time fields to TypeScript Date, but the Kubernetes API returns ISO 8601 strings and @openshift-console/dynamic-plugin-sdk expects creationTimestamp and deletionTimestamp as string. After running the update scripts, this must be fixed.
Run the post-generation fix script (if it exists):
npm run fix:timestamps
If the script does not exist, manually fix all ObjectMeta types:
for f in $(grep -rl "Timestamp?: Date" src/generated/); do
sed -i '' \
-e 's/creationTimestamp?: Date;/creationTimestamp?: string;/' \
-e 's/deletionTimestamp?: Date;/deletionTimestamp?: string;/' \
-e "s/(new Date(json\['creationTimestamp'\]))/json['creationTimestamp']/" \
-e "s/(new Date(json\['deletionTimestamp'\]))/json['deletionTimestamp']/" \
-e "s/((value\['creationTimestamp'\]).toISOString())/value['creationTimestamp']/" \
-e "s/((value\['deletionTimestamp'\]).toISOString())/value['deletionTimestamp']/" \
"$f"
echo "Fixed: $f"
done
This step is mandatory. Skipping it causes ~370 TypeScript errors in the consumer project where Forklift CRD types become incompatible with the Console SDK's K8sResourceCommon.
3f. Build verification
npm run build
npm run lint
If the build fails with "Duplicate identifier" errors:
- Parse the error to identify the conflicting type name
- Remove it from the kubevirt selective export in
src/generated/index.ts
- Rebuild
Repeat until the build passes or a non-conflict error is encountered.
If a non-conflict error occurs, present it to the user and stop for manual resolution.
Phase 4: Version Bump, Tracking, and PR
4a. Bump version
Edit $TYPES_REPO_DIR/package.json to increment the version (default: patch bump).
4b. Update Version Tracking
Edit $TYPES_REPO_DIR/MAINTENANCE.md, updating the Version Tracking table. For each source that was updated, set:
- Current Version: the version/tag used. For Forklift, use
main (<short-sha>) (e.g. main (a1b2c3d)) to record the exact commit.
- Last Updated: today's date in
YYYY-MMM-DD format (e.g. 2026-APR-28)
- Updated By:
aturgema (made with cursor)
4c. Commit
Collect git user info:
GIT_NAME=$(git config user.name)
GIT_EMAIL=$(git config user.email)
git add -A
git commit -m "$(cat <<'EOF'
chore: update types to <NEW_VERSION>
Updated sources:
- Forklift: <version> (CRDs + inventory types)
- Kubernetes: <version>
- KubeVirt: <version>
- CDI: <version>
Resolves: <JIRA_KEY>
Signed-off-by: <GIT_NAME> <<GIT_EMAIL>>
EOF
)"
4d. Push and create PR
git push -u origin chore/types-update-<NEW_VERSION>
gh pr create \
--repo kubev2v/forklift-console-types \
--base main \
--title "chore: update types to <NEW_VERSION>" \
--body "$(cat <<'EOF'
## Summary
- Updated @forklift-ui/types from <OLD_VERSION> to <NEW_VERSION>
- Sources updated: Forklift (<ver>), Kubernetes (<ver>), KubeVirt (<ver>), CDI (<ver>)
## Verification
- [x] `npm run build` passes
- [x] `npm run lint` passes
- [x] `npm run check:conflicts` shows no unresolved conflicts
## Links
> Jira: https://issues.redhat.com/browse/<JIRA_KEY>
Resolves: <JIRA_KEY>
EOF
)"
GATE 2: Wait for types PR merge
Present the PR URL to the user. Ask:
- "PR is merged, proceed with release" -> continue to Phase 5
- "Still waiting" -> pause and remind later
- "Cancel" -> abort
Do NOT proceed to Phase 5 until the user explicitly confirms the PR is merged.
Phase 5: Create GitHub Release and Wait for npm
5a. Create the release
gh release create "v<NEW_VERSION>" \
--repo kubev2v/forklift-console-types \
--title "v<NEW_VERSION>" \
--generate-notes
This creates a tag and publishes a release, which triggers the release.yml GitHub Action to publish to npm.
5b. Wait for npm publish
Run the polling script:
.cursor/skills/types-update/scripts/poll-npm.sh <NEW_VERSION>
This polls npm view @forklift-ui/types version every 30 seconds for up to 10 minutes.
If the poll times out, notify the user:
The new version <NEW_VERSION> has not appeared on npm after 10 minutes.
Check the GitHub Action logs:
https://github.com/kubev2v/forklift-console-types/actions/workflows/release.yml
If the version appears, proceed to Phase 6.
Phase 6: Update Consumer Project (forklift-console-plugin)
6a. Create branch
cd /path/to/forklift-console-plugin
git checkout main
git pull upstream main
git checkout -b chore/bump-types-<NEW_VERSION>
6b. Install new version
npm install @forklift-ui/types@<NEW_VERSION>
6c. Verify no breaking changes
npm run lint
npx tsc --noEmit
If errors occur
Do NOT create a PR. Instead:
- Present the full error output to the user
- Analyze the errors -- common causes:
- Removed type: a type was dropped from the package. Search for its usage and suggest the replacement.
- Changed field: a field was renamed or its type changed. Show the old vs new type definition.
- New required field: a type now requires a field that wasn't there before.
- Suggest fixes if possible
- Wait for user to resolve. After resolution, re-run verification.
If clean
Commit and create PR:
git add package.json package-lock.json
git commit -m "$(cat <<'EOF'
chore: bump @forklift-ui/types to <NEW_VERSION>
Resolves: <JIRA_KEY>
EOF
)"
git push -u origin chore/bump-types-<NEW_VERSION>
gh pr create \
--title "chore: bump @forklift-ui/types to <NEW_VERSION>" \
--body "$(cat <<'EOF'
## Summary
- Bumps `@forklift-ui/types` from `<OLD_VERSION>` to `<NEW_VERSION>`
- No breaking changes detected (lint and tsc pass)
## Links
> Types PR: <types-pr-url>
> Jira: https://issues.redhat.com/browse/<JIRA_KEY>
Resolves: <JIRA_KEY>
EOF
)"
Phase 7: Monitor CI
7a. Initial wait
Sleep for 10 minutes after creating the consumer PR:
sleep 600
7b. Check PR status
gh pr checks <PR_NUMBER>
7c. Act on results
7d. Handle failures
After presenting the failure summary, use the AskQuestion tool to ask the user:
-
"Fix it for me" -- The agent enters a fix-verify loop:
- Analyze the failure logs and identify the root cause
- Apply the fix (edit code, update config, etc.)
- Run local verification (
npm run lint, npx tsc --noEmit, npm test as appropriate)
- Commit, push, and go back to step 7a (wait then re-check CI)
- If the fix attempt fails or the agent cannot determine a fix, fall back to presenting the error and asking again
-
"I'll fix it myself" -- The agent pauses and waits for the user to signal readiness:
- Present the failure details and stop
- When the user indicates they are done (clicks the option or sends a message), go back to step 7a (wait then re-check CI)
-
"Skip / Stop monitoring" -- End Phase 7. The PR is left as-is for manual handling.
Continue the fix-verify loop until all checks pass or the user chooses to stop.
Important Notes
- The types repo uses npm Trusted Publishing (
--provenance). No npm token is needed; the GitHub Action handles auth via OIDC.
- Forklift always targets
main (never a tagged release) to pick up the latest CRD and inventory type changes. Record the commit SHA in MAINTENANCE.md for reproducibility.
- Kubernetes, KubeVirt, and CDI use stable tagged releases (e.g.,
v1.32.0).
- The
openapi-generator-cli requires a Java runtime. If npx openapi-generator-cli fails with a Java error, the user needs to install JDK 11+.
- After updating,
swagger.json files are kept in the repo (they are large but committed). This is intentional for reproducibility.