Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
f0cad5e
feat: Add config-manager push authentication command
github-actions[bot] Mar 19, 2026
3ce0613
feat: Add config-manager push connector-definitions command
dallinjsevy Jun 25, 2023
368a903
feat: add config-manager-pull command for custom nodes
brycentrivir Apr 13, 2026
da988b8
add missing help menus for config-manager commands
d-rex14 Mar 19, 2026
8d9ad61
do not export scheduler objects provided by the API
devintrivir Mar 27, 2026
80b5a8e
Updated changelog and version for release v4.0.0-45
github-actions[bot] Apr 17, 2026
d62d94e
Initial plan
Copilot Apr 19, 2026
7c72803
feat: add label-driven release bumping and integration batching workflow
Copilot Apr 19, 2026
1e3af6b
chore: address workflow/docs review nits
Copilot Apr 19, 2026
55d0a67
Merge pull request #595 from rockcarver/copilot/implement-label-drive…
vscheuber Apr 19, 2026
d91603d
Initial plan
Copilot Apr 19, 2026
75e5f78
fix: emit compact JSON outputs in integration-batch workflow
Copilot Apr 19, 2026
36a5b4b
Merge pull request #596 from rockcarver/copilot/fix-integration-batch…
vscheuber Apr 19, 2026
7dfc342
Initial plan
Copilot Apr 19, 2026
8e4c80e
fix: use check-runs criteria for integration batch selection
Copilot Apr 19, 2026
3ddfccb
chore: harden check-runs parsing in integration workflow
Copilot Apr 19, 2026
bfa4bc2
Merge pull request #597 from rockcarver/copilot/update-integration-ba…
vscheuber Apr 19, 2026
0a790fb
Initial plan
Copilot Apr 19, 2026
ee718fb
feat: automate integration batch merge conflict recovery
Copilot Apr 19, 2026
bfd9ba9
fix: address review feedback for integration merge automation
Copilot Apr 19, 2026
ed1f6bb
fix: apply PR review feedback for merge script output handling
Copilot Apr 19, 2026
b054b42
Merge pull request #598 from rockcarver/copilot/implement-scripted-co…
vscheuber Apr 19, 2026
03b49e6
Initial plan
Copilot Apr 19, 2026
500a411
fix: auto-regenerate lockfile on integration merge conflicts
Copilot Apr 19, 2026
905b915
chore: simplify snapshot conflict condition
Copilot Apr 19, 2026
f58b157
fix: add lockfile regeneration failure handling
Copilot Apr 19, 2026
0999ca9
chore: refine lockfile regeneration diagnostics
Copilot Apr 19, 2026
e7c9c5c
Update .github/integration/scripts/merge_prs.sh
vscheuber Apr 19, 2026
d122f0b
Update .github/workflows/integration-batch.yml
vscheuber Apr 19, 2026
09251ff
fix: report lockfile auto-resolution from regeneration summary
Copilot Apr 19, 2026
8e2a7de
Merge pull request #600 from rockcarver/copilot/create-clean-delta-fo…
vscheuber Apr 19, 2026
a381b66
Initial plan
Copilot Apr 19, 2026
8d7045a
fix: prepare frodo cli before snapshot updates in integration batching
Copilot Apr 19, 2026
fc9794a
fix: rebuild frodo before each snapshot update batch
Copilot Apr 20, 2026
9a3b688
fix: refresh frodo install on each snapshot update pass
Copilot Apr 20, 2026
5ad5b78
Merge pull request #601 from rockcarver/copilot/fix-integration-batch…
vscheuber Apr 20, 2026
f257f92
refactor: remove unused printMessage import from config-manager-pull-…
vscheuber Apr 20, 2026
a8979ef
Initial plan
Copilot Apr 20, 2026
5007543
feat: run post-push integration tests in integration-batch workflow
Copilot Apr 20, 2026
660825f
chore: harden npm global bin resolution in integration test step
Copilot Apr 20, 2026
841e8cd
Merge pull request #603 from rockcarver/copilot/update-integration-ba…
vscheuber Apr 20, 2026
574df3c
Merge PR #589: feat: Add config-manager push authentication command
github-actions[bot] Apr 20, 2026
34d8685
Merge PR #590: feat: Add config-manager push connector-definitions co…
github-actions[bot] Apr 20, 2026
32b1134
test: update snapshots for integration conflict resolution
github-actions[bot] Apr 20, 2026
b443b1a
Merge PR #592: Add config-manager-pull command for custom nodes
github-actions[bot] Apr 20, 2026
10122b7
Merge PR #593: add missing help menus for config-manager commands
github-actions[bot] Apr 20, 2026
69c45be
Merge PR #594: do not export scheduler objects provided by the API
github-actions[bot] Apr 20, 2026
f542499
Updated changelog and version for release v4.0.0-46
github-actions[bot] Apr 20, 2026
a8f44ba
Merge pull request #604 from rockcarver/integration
vscheuber Apr 20, 2026
8ffc2fe
Updated changelog and version for release v4.0.0-47
github-actions[bot] Apr 20, 2026
d5e93f4
Initial plan
Copilot Apr 20, 2026
60ae871
ci: label integration PR when snapshot updates are present
Copilot Apr 20, 2026
c58071e
ci: add snapshot-review label to generated integration PR
Copilot Apr 20, 2026
71b8ed1
Update .github/workflows/integration-batch.yml
vscheuber Apr 21, 2026
955d7e2
Merge pull request #606 from rockcarver/copilot/add-snapshot-review-l…
vscheuber Apr 21, 2026
a088b07
Updated changelog and version for release v4.0.0-48
github-actions[bot] Apr 21, 2026
e372807
-n flag added to secret pull
devintrivir Apr 3, 2026
dea6f7d
-a flag added to secret pull
devintrivir Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
403 changes: 403 additions & 0 deletions .github/integration/scripts/merge_prs.sh

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions .github/integration/union-allowlist-extra.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# One path per line. Paths listed here are added to the generated union allowlist.
# Example:
# src/cli/example/example-command.ts
3 changes: 3 additions & 0 deletions .github/integration/union-blocklist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# One path per line. Paths listed here are removed from the generated union allowlist.
# Example:
# src/cli/example/example-command.ts
336 changes: 336 additions & 0 deletions .github/workflows/integration-batch.yml

Large diffs are not rendered by default.

103 changes: 78 additions & 25 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,37 +44,90 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

- name: Update package-log.json before version bump
- name: Update package-lock.json before version bump
run: npm i --package-lock-only

- name: Install dependencies
run: npm ci

- name: 'Prepare Version Bump'
id: version-bump
uses: 'phips28/gh-action-bump-version@master'
with:
major-wording: 'MAJOR RELEASE'
minor-wording: 'MINOR RELEASE'
patch-wording: 'PATCH RELEASE'
rc-wording: ''
tag-prefix: 'v'
default: prerelease
preid: ''
bump-policy: 'ignore'
skip-commit: 'true'
skip-tag: 'true'
skip-push: 'true'
- name: 'Determine Release Bump Type'
id: release-bump
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Update package-log.json after version bump
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
SHA: ${{ github.sha }}
run: |
if [ "${{ github.event_name }}" != "push" ] || [ "${{ github.ref }}" != "refs/heads/main" ]; then
echo "bumpType=prerelease" >> "$GITHUB_OUTPUT"
exit 0
fi

prs="$(gh api -H "Accept: application/vnd.github+json" "/repos/$REPO/commits/$SHA/pulls")"
pr_count="$(echo "$prs" | jq 'length')"
if [ "$pr_count" -ne 1 ]; then
echo "::error::Expected exactly one associated PR for commit $SHA on main, found $pr_count."
exit 1
fi

release_labels="$(echo "$prs" | jq -r '.[0].labels[].name | select(. == "release:patch" or . == "release:minor" or . == "release:major")')"
label_count="$(echo "$release_labels" | sed '/^$/d' | wc -l | tr -d ' ')"
if [ "$label_count" -gt 1 ]; then
echo "::error::PR has multiple release labels. Use exactly one of release:patch, release:minor, release:major."
exit 1
fi
if [ "$label_count" -eq 0 ]; then
echo "bumpType=prerelease" >> "$GITHUB_OUTPUT"
exit 0
fi

label="$(echo "$release_labels" | head -n 1)"
case "$label" in
"release:patch") echo "bumpType=patch" >> "$GITHUB_OUTPUT" ;;
"release:minor") echo "bumpType=minor" >> "$GITHUB_OUTPUT" ;;
"release:major") echo "bumpType=major" >> "$GITHUB_OUTPUT" ;;
*) echo "::error::Unsupported release label: $label"; exit 1 ;;
esac

- name: 'Apply Version Bump'
id: version-bump
run: |
current_version="$(node -p "require('./package.json').version")"
bump_type="${{ steps.release-bump.outputs.bumpType }}"
if [ -z "$bump_type" ]; then
echo "::error::Missing bump type."
exit 1
fi

case "$bump_type" in
prerelease)
new_version="$(node -p "require('semver').inc('$current_version','prerelease')")"
;;
patch|minor|major)
new_version="$(node -p "require('semver').inc('$current_version','$bump_type')")"
;;
*)
echo "::error::Unsupported bump type: $bump_type"
exit 1
;;
esac

if [ -z "$new_version" ] || [ "$new_version" = "null" ]; then
echo "::error::Failed to compute new version from $current_version using bump type $bump_type."
exit 1
fi

npm version "$new_version" --no-git-tag-version --allow-same-version
echo "newTag=v$new_version" >> "$GITHUB_OUTPUT"
echo "newVersion=$new_version" >> "$GITHUB_OUTPUT"
if echo "$new_version" | grep -q '-'; then
echo "preRelease=true" >> "$GITHUB_OUTPUT"
else
echo "preRelease=false" >> "$GITHUB_OUTPUT"
fi

- name: Update package-lock.json after version bump
run: npm i --package-lock-only

- name: 'Version From Tag'
id: version-from-tag
run: echo "version=$(echo '${{ steps.version-bump.outputs.newTag }}' | sed 's/v//')" >> "$GITHUB_OUTPUT"

- name: Build frodo-cli
run: |
npm run build
Expand All @@ -98,8 +151,8 @@ jobs:

outputs:
newTag: ${{ steps.version-bump.outputs.newTag }}
newVersion: ${{ steps.version-from-tag.outputs.version }}
preRelease: ${{ contains(steps.version-bump.outputs.newTag, '-') }}
newVersion: ${{ steps.version-bump.outputs.newVersion }}
preRelease: ${{ steps.version-bump.outputs.preRelease }}

test:
name: 'Test'
Expand Down
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [4.0.0-48] - 2026-04-21

## [4.0.0-47] - 2026-04-20

## [4.0.0-46] - 2026-04-20

## [4.0.0-45] - 2026-04-17

## [4.0.0-44] - 2026-04-17

## [4.0.0-43] - 2026-04-08
Expand Down Expand Up @@ -2251,7 +2259,11 @@ Frodo CLI 2.x automatically refreshes session and access tokens before they expi
- Fixed problem with adding connection profiles
- Miscellaneous bug fixes

[unreleased]: https://github.com/rockcarver/frodo-cli/compare/v4.0.0-44...HEAD
[unreleased]: https://github.com/rockcarver/frodo-cli/compare/v4.0.0-48...HEAD
[4.0.0-48]: https://github.com/rockcarver/frodo-cli/compare/v4.0.0-47...v4.0.0-48
[4.0.0-47]: https://github.com/rockcarver/frodo-cli/compare/v4.0.0-46...v4.0.0-47
[4.0.0-46]: https://github.com/rockcarver/frodo-cli/compare/v4.0.0-45...v4.0.0-46
[4.0.0-45]: https://github.com/rockcarver/frodo-cli/compare/v4.0.0-44...v4.0.0-45
[4.0.0-44]: https://github.com/rockcarver/frodo-cli/compare/v4.0.0-43...v4.0.0-44
[4.0.0-43]: https://github.com/rockcarver/frodo-cli/compare/v4.0.0-42...v4.0.0-43
[4.0.0-42]: https://github.com/rockcarver/frodo-cli/compare/v4.0.0-41...v4.0.0-42
Expand Down
6 changes: 5 additions & 1 deletion docs/CONTRIBUTE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ OR

### Prerequisites

- Node.js 18 or later, 20 or 22 recommended
- Node.js 20 or later (Node.js 24 recommended)
- npm (included with Node.js)
- A GUI editor is highly recommended. The current developers use [VSCode](https://code.visualstudio.com/), but you are welcome to others, like [Atom](https://atom.io/) or [Sublime](https://www.sublimetext.com/) too. The repository contains configuration files for VSCode's [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [prettier](https://prettier.io/) add-ons, which will automatically lint the code and apply coding styles when using VSCode. The same files may work for other editors with similar add-ons, but this has not been tested.

Expand Down Expand Up @@ -99,6 +99,10 @@ npm run lint
npm test
```

## Integration batching

Maintainers can batch eligible PRs into the `integration` branch using the [`integration-batch` workflow](../.github/workflows/integration-batch.yml). See [INTEGRATION.md](./INTEGRATION.md) for label semantics and workflow behavior.

### Code structure and conventions

Frodo CLI adheres to the following folder and file structure:
Expand Down
60 changes: 60 additions & 0 deletions docs/INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Integration Batching Workflow

The [`integration-batch` workflow](../.github/workflows/integration-batch.yml) automates integration branch preparation by batching selected pull requests.

## Labels

- `integration-batch`: PR is queued for integration batching.
- `integrated`: PR was successfully merged into the `integration` branch by automation.
- `integration-failed`: automation attempted integration but hit a merge conflict.
- `integration-needs-snapshot-review`: applied to the generated `integration -> main` PR when snapshot auto-recovery updates snapshot files.

## Selection rules

The workflow selects PRs that are:

- open
- non-draft
- labeled `integration-batch`
- not labeled `integrated`
- with check runs present for the PR head SHA
- with all check runs in `completed` status
- with all completed check runs concluded as `success`, `skipped`, or `neutral`

## Merge behavior

Each run rebuilds `integration` from `main`, then attempts merge commits (`git merge --no-ff`) for selected PRs.

- On successful merge:
- remove `integration-batch`
- remove `integration-failed` (if present)
- add `integrated`
- add a comment including the run URL
- On merge conflict:
- if all conflicted files are in the auto-generated union allowlist, conflicts are auto-resolved by union merge
- if conflicts are snapshot-only (`*.snap` / `__snapshots__`, optionally with `package-lock.json`), merge is completed and targeted `npm run test:update <pattern>` commands are executed, then updated snapshots are committed to `integration`
- if `package-lock.json` is conflicted (including lockfile-only conflicts), automation checks out one side to complete the merge, runs `npm i --package-lock-only`, and commits the regenerated lockfile to `integration`
- otherwise merge is aborted, `integration-failed` is applied, and the author is asked to rebase and re-add `integration-batch`

Union allowlist generation runs on every workflow execution:

- includes `src/cli/**` files containing `const program = new FrodoStubCommand(`
- excludes any file containing `const program = new FrodoCommand(`
- applies manual overrides from:
- `.github/integration/union-allowlist-extra.txt`
- `.github/integration/union-blocklist.txt`

After merges, the workflow updates `@rockcarver/frodo-lib` to `@next`, commits lockfile changes when needed, pushes `integration`, runs the full test suite on `integration`, and then creates or updates an `integration -> main` PR titled `integration`.

Because `integration` is pushed before the full test suite runs, the branch can be temporarily red until the test stage finishes.

## Dry run mode

When manually triggered, set `dry_run=true` to simulate batching without side effects:

- does not push `integration`
- does not create/update the integration PR
- does not edit labels or post PR comments
- does not run the post-push full test suite on `integration`

The workflow still computes candidate PRs and attempts merges locally so maintainers can validate batchability before a real run.
29 changes: 17 additions & 12 deletions docs/PIPELINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ This information is only actionable if you are an active contributor or maintain

Frodo CLI adopted the principle of continuous integration. Therefore every push to the main branch in the [rockcarver/frodo-cli] repository triggers the automated release pipeline.

The pipeline determines the type of release - `prerelease`, `patch`, `minor`, `major` - for the push:

- Scans the commit and PR comments for trigger phrases:
- `PATCH RELEASE` triggers a `patch` release
- `MINOR RELEASE` triggers a `minor` release
- `MAJOR RELEASE` triggers a `major` release
- Everything else triggers a `prerelease`
The pipeline determines the type of release - `prerelease`, `patch`, `minor`, `major` - for the push by looking up the pull request associated with the merged commit on `main`:

- Uses exactly one merged PR associated with the pushed commit SHA.
- Uses PR labels:
- `release:patch` triggers a `patch` release
- `release:minor` triggers a `minor` release
- `release:major` triggers a `major` release
- No release label triggers a `prerelease`
- Fails if:
- no PR (direct push to `main`) is associated with the commit
- more than one PR is associated with the commit
- more than one `release:*` label is present on the merged PR
- Bumps the version accordingly:<br>
`<major>`.`<minor>`.`<patch>`-`<prerelease>`
- Updates the [changelog](../CHANGELOG.md) file in [keep a changelog](https://keepachangelog.com/en/1.0.0/) format:
Expand All @@ -27,19 +32,19 @@ The pipeline determines the type of release - `prerelease`, `patch`, `minor`, `m
- Adds release details links

❗❗❗ IMPORTANT ❗❗❗<br>
Contributors are instructed to submit pull requests. Maintainers must make sure none of the commit comments nor the PR comment contain trigger phrases that would cause the pipeline to perform an undesired version bump and release.
Contributors are instructed to submit pull requests. Direct pushes to `main` are not supported by the automated release version-selection logic and will fail.

### Automatic Pre-Releases During Iterative Development

The default release type (if no specific and exact trigger phrases are used) results in a pre-release. Pre-releases are flagged with the label `Pre-release` on the [release page](../releases) indicating to users that these releases are not considered final or complete.
The default release type (if no specific release label is present) results in a pre-release. Pre-releases are flagged with the label `Pre-release` on the [release page](../releases) indicating to users that these releases are not considered final or complete.

Pre-releases are a great way to publish the latest and greatest functionality but they are not fully polished, readme and changelog might not be updated and test coverage might not be complete.

### Triggering Patch, Minor, and Major Releases

Maintainers must validate PRs contain an updated `Unreleased` section in the[changelog](../CHANGELOG.md) before merging any PR. Changelog entries must adhere to the [keep a changelog](https://keepachangelog.com/en/1.0.0/) format.
Maintainers must validate PRs contain an updated `Unreleased` section in the [changelog](../CHANGELOG.md) before merging any PR. Changelog entries must adhere to the [keep a changelog](https://keepachangelog.com/en/1.0.0/) format.

Maintainers must use an appropriate trigger phrase (see: [Every Push Triggers A Release](#Every-Push-Triggers-A-Release)) in the PR title to trigger the appropriate automated version bump and release.
Maintainers must add an appropriate release label (see: [Every Push Triggers A Release](#Every-Push-Triggers-A-Release)) to the PR before merge to trigger the automated version bump and release.

❗❗❗ IMPORTANT ❗❗❗<br>
Maintainers must adhere to the [guidelines set forth by the npm project](https://docs.npmjs.com/about-semantic-versioning#incrementing-semantic-versions-in-published-packages) to determine the appropriate release type:
Expand Down Expand Up @@ -112,7 +117,7 @@ So to recover from that, the following needs to happen:
- [package-lock.json](../package-lock.json)
- Find the 2 occurances of the faulty version in package-lock.json and reset them to the `previous version` from before the faulty version bump
3. Commit your changes and create a new pull request
4. In the frodo repository, merge the PR and provide the appropriate comment to trigger the intended version bump
4. In the frodo repository, merge the PR and provide the appropriate release label (`release:patch`, `release:minor`, or `release:major`) to trigger the intended version bump
5. Remove the faulty release from npmjs.com
This is important as without this step the faulty release will remain published on [npmjs.com](https://www.npmjs.com/package/@rockcarver/frodo-cli) (npm registry).
- You must be a maintainer of the package on npmjs.com.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rockcarver/frodo-cli",
"version": "4.0.0-44",
"version": "4.0.0-48",
"type": "module",
"description": "A command line interface to manage ForgeRock Identity Cloud tenants, ForgeOps deployments, and classic deployments.",
"keywords": [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { frodo } from '@rockcarver/frodo-lib';
import { Option } from 'commander';

import { configManagerExportCustomNodes } from '../../../configManagerOps/FrConfigCustomNodesOps';
import { getTokens } from '../../../ops/AuthenticateOps';
import { verboseMessage } from '../../../utils/Console';
import { FrodoCommand } from '../../FrodoCommand';

const { CLOUD_DEPLOYMENT_TYPE_KEY, FORGEOPS_DEPLOYMENT_TYPE_KEY } =
frodo.utils.constants;

const deploymentTypes = [
CLOUD_DEPLOYMENT_TYPE_KEY,
FORGEOPS_DEPLOYMENT_TYPE_KEY,
];

export default function setup() {
const program = new FrodoCommand(
'frodo config-manager pull custom-nodes',
[],
deploymentTypes
);

program
.description('Export custom nodes.')
.addOption(
new Option(
'-n, --node-name <node-name>',
'Custom node display name. If specified, only one custom node is exported.'
)
)
.action(async (host, realm, user, password, options, command) => {
command.handleDefaultArgsAndOpts(
host,
realm,
user,
password,
options,
command
);

if (await getTokens(false, true, deploymentTypes)) {
if (options.nodeName) {
verboseMessage(
`Fetching custom node with name '${options.nodeName}'`
);
} else {
verboseMessage('Fetching custom nodes');
}
const outcome = await configManagerExportCustomNodes(options.nodeName);
if (!outcome) process.exitCode = 1;
} else {
process.exitCode = 1;
}
});

return program;
}
Loading