diff --git a/Makefile b/Makefile index d21b2c80..2ceed9aa 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ endef .PHONY: help default install fmt vet test importfmtlint golangcilint devcheck devchecknotest .PHONY: starttestcontainer removetestcontainer spincontainer openlocalwebapi openapp protogen .PHONY: _check_env _check_ping_env _check_docker _run_pf_container _wait_for_pf _stop_pf_container +.PHONY: generate-options-docs generate-command-docs generate-all-docs # ==================================================================================== # USER-FACING COMMANDS @@ -83,6 +84,34 @@ golangcilint: ## Run golangci-lint for comprehensive code analysis $(GOLANGCI_LINT) run --timeout 5m ./... echo "✅ No linting issues found." +generate-options-docs: ## Generate configuration options documentation then validate via golden tests + @echo " > Docs: Generating options documentation..." + @if [ -z "$(OUTPUT)" ]; then \ + mkdir -p ./docs/dev-ux-portal-docs/general; \ + $(GOCMD) run ./tools/generate-options-docs -asciidoc -o ./docs/dev-ux-portal-docs/general/cli-configuration-settings-reference.adoc; \ + echo "✅ Documentation generated at docs/dev-ux-portal-docs/general/cli-configuration-settings-reference.adoc"; \ + else \ + $(GOCMD) run ./tools/generate-options-docs $(OUTPUT); \ + echo "✅ Documentation generated with custom OUTPUT $(OUTPUT)"; \ + fi + @echo " > Docs: Running golden tests for options docs..." + @$(GOCMD) test ./tools/generate-options-docs/docgen -run TestOptionsDocGeneration >/dev/null && echo "✅ Options documentation golden test passed." + +generate-command-docs: ## Generate per-command AsciiDoc pages then validate via golden tests + @echo " > Docs: Generating command documentation..." + mkdir -p ./docs/dev-ux-portal-docs + $(GOCMD) run ./tools/generate-command-docs -o ./docs/dev-ux-portal-docs $(COMMAND_DOCS_ARGS) + echo "✅ Command docs generated in docs/dev-ux-portal-docs" + @echo " > Docs: Running golden tests for command docs..." + @$(GOCMD) test ./tools/generate-command-docs -run TestCommandDocGeneration >/dev/null && echo "✅ Command documentation golden test passed." + +generate-all-docs: ## Rebuild ALL docs then run golden tests for both sets + @echo " > Docs: Rebuilding all documentation (clean + regenerate)..." + mkdir -p ./docs/dev-ux-portal-docs/general + $(MAKE) generate-options-docs OUTPUT='-o docs/dev-ux-portal-docs/general/cli-configuration-settings-reference.adoc' + $(MAKE) generate-command-docs + @echo "✅ All documentation rebuilt and validated via golden tests." + protogen: ## Generate Go code from .proto files @echo " > Protogen: Generating gRPC code from proto files..." protoc --proto_path=./internal/proto --go_out=./internal --go-grpc_out=./internal ./internal/proto/*.proto diff --git a/README.md b/README.md index c0dad799..3f38fb5d 100644 --- a/README.md +++ b/README.md @@ -10,17 +10,17 @@ The Ping CLI is a unified command line interface for configuring and managing Pi ## Table of Contents - [Install](#install) - - [Docker](#docker) - - [macOS](#macos) - - [Linux](#linux) - - [Windows](#windows) + - [Docker](#docker) + - [macOS](#macos) + - [Linux](#linux) + - [Windows](#windows) - [Verify](#verify) - - [Checksums](#checksums) - - [GPG Signatures](#gpg-signatures) + - [Checksums](#checksums) + - [GPG Signatures](#gpg-signatures) - [Configure Ping CLI](#configure-ping-cli) - [Commands](#commands) - - [Platform Export](#platform-export) - - [Custom Request](#custom-request) + - [Platform Export](#platform-export) + - [Custom Request](#custom-request) - [Getting Help](#getting-help) ## Install @@ -30,11 +30,13 @@ The Ping CLI is a unified command line interface for configuring and managing Pi Use the [Ping CLI Docker image](https://hub.docker.com/r/pingidentity/pingcli) Pull Image: + ```shell docker pull pingidentity/pingcli:latest ``` Example Commands: + ```shell docker run --rm pingidentity/pingcli:latest @@ -43,7 +45,7 @@ docker run --rm pingidentity/pingcli:latest --version ### macOS -##### Homebrew +#### Homebrew Use PingIdentity's Homebrew tap to install Ping CLI @@ -52,7 +54,7 @@ brew tap pingidentity/tap brew install pingcli ``` -##### Manual Installation +#### Manual Installation See [the latest GitHub release](https://github.com/pingidentity/pingcli/releases/latest) for artifact downloads, artifact signatures, and the checksum file. To verify package downloads, see the [Verify Section](#verify). @@ -86,6 +88,7 @@ brew install pingcli See [the latest GitHub release](https://github.com/pingidentity/pingcli/releases/latest) for Alpine (.apk) package downloads. To verify package downloads, see the [Verify Section](#verify). > **_NOTE:_** The following commands may require `sudo` if not run as the root user. + ```shell apk add --allow-untrusted ./pingcli__linux_amd64.apk apk add --allow-untrusted ./pingcli__linux_arm64.apk @@ -96,6 +99,7 @@ apk add --allow-untrusted ./pingcli__linux_arm64.apk See [the latest GitHub release](https://github.com/pingidentity/pingcli/releases/latest) for Debian (.deb) package downloads. To verify package downloads, see the [Verify Section](#verify). > **_NOTE:_** The following commands may require `sudo` if not run as the root user. + ```shell apt install ./pingcli__linux_amd64.deb apt install ./pingcli__linux_arm64.deb @@ -106,6 +110,7 @@ apt install ./pingcli__linux_arm64.deb See [the latest GitHub release](https://github.com/pingidentity/pingcli/releases/latest) for RPM (.rpm) package downloads. To verify package downloads, see the [Verify Section](#verify). > **_NOTE:_** The following commands may require `sudo` if not run as the root user. + ```shell yum install ./pingcli__linux_amd64.rpm yum install ./pingcli__linux_arm64.rpm @@ -114,17 +119,19 @@ yum install ./pingcli__linux_arm64.rpm OR > **_NOTE:_** The following commands may require `sudo` if not run as the root user. + ```shell dnf install ./pingcli__linux_amd64.rpm dnf install ./pingcli__linux_arm64.rpm ``` > **_NOTE:_** + > - Use `yum` for CentOS/RHEL 7 and earlier, and for older Fedora systems. > - Use `dnf` for Fedora 22+ and CentOS/RHEL 8+. > Both commands achieve the same result; use the one appropriate for your distribution. -##### Manual Installation +#### Manual Installation See [the latest GitHub release](https://github.com/pingidentity/pingcli/releases/latest) for artifact downloads, artifact signatures, and the checksum file. To verify package downloads, see the [Verify Section](#verify). @@ -144,7 +151,7 @@ sudo mv pingcli /usr/local/bin/pingcli; ### Windows -##### Manual Installation +#### Manual Installation See [the latest GitHub release](https://github.com/pingidentity/pingcli/releases/latest) for artifact downloads, artifact signatures, and the checksum file. To verify package downloads, see the [Verify Section](#verify). @@ -152,6 +159,7 @@ OR Use the following single-line PowerShell 7.4 command to install Ping CLI into '%LOCALAPPDATA%\Programs' directly. >**_NOTE:_** After installation, ensure that `%LOCALAPPDATA%\Programs` is included in your PATH environment variable. If it is not already present, add it so you can call `pingcli` directly in your terminal. + ```powershell $latestReleaseUrl = Invoke-WebRequest -Uri "https://github.com/pingidentity/pingcli/releases/latest" -MaximumRedirection 0 -ErrorAction Ignore -UseBasicParsing -SkipHttpErrorCheck; ` $RELEASE_VERSION = [System.IO.Path]::GetFileName($latestReleaseUrl.Headers.Location); ` @@ -172,7 +180,7 @@ See [the latest GitHub release](https://github.com/pingidentity/pingcli/releases See [the latest GitHub release](https://github.com/pingidentity/pingcli/releases/latest) for the artifact downloads and signature files. -##### Add our public GPG Key via OpenPGP Public Key Server +#### Add our public GPG Key via OpenPGP Public Key Server ```shell gpg --keyserver keys.openpgp.org --recv-key 0x6703FFB15B36A7AC @@ -181,6 +189,7 @@ gpg --keyserver keys.openpgp.org --recv-key 0x6703FFB15B36A7AC OR ##### Add our public GPG Key via MIT PGP Public Key Server + ```shell gpg --keyserver pgp.mit.edu --recv-key 0x6703FFB15B36A7AC ``` @@ -216,6 +225,12 @@ and their purposes. See [Autocompletion Documentation](./docs/autocompletion/autocompletion.md) for information on loading autocompletion for select command flags. +### Documentation Generation + +Documentation generation instructions (configuration options reference, per-command pages, navigation, rebuild workflow, and golden test usage) have moved to a dedicated guide: + +See: `tools/README_DocumentGeneration.md` + ## Commands Ping CLI commands have the following structure: diff --git a/docs/dev-ux-portal-docs/general/cli-configuration-settings-reference.adoc b/docs/dev-ux-portal-docs/general/cli-configuration-settings-reference.adoc new file mode 100644 index 00000000..0dc41501 --- /dev/null +++ b/docs/dev-ux-portal-docs/general/cli-configuration-settings-reference.adoc @@ -0,0 +1,87 @@ += Configuration Settings Reference +:created-date: March 23, 2025 +:revdate: September 24, 2025 +:resourceid: pingcli_configuration_settings_reference + +The following configuration settings can be applied when using Ping CLI. + +The following configuration settings can be applied by using the xref:command_reference:pingcli_config_set.adoc[`config set` command] to persist the configuration value for a given **Configuration Key** in the Ping CLI configuration file. + +The configuration file is created at `.pingcli/config.yaml` in the user's home directory. + +== General Properties + +[cols="2,2,2,1,3"] +|=== +|Configuration Key |Equivalent Parameter |Environment Variable |Data Type |Purpose + +| `activeProfile` | | | String | +| `description` | | | String | +| `detailedExitCode` | `--detailed-exitcode` / `-D` | PINGCLI_DETAILED_EXITCODE | Boolean | Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. +| `noColor` | `--no-color` | PINGCLI_NO_COLOR | Boolean | Disable text output in color. (default false) +| `outputFormat` | `--output-format` / `-O` | PINGCLI_OUTPUT_FORMAT | String (Enum) | Specify the console output format. (default text) Options are: json, text. +| `plugins` | | | String Array | +|=== + +== Ping Identity Platform Service Properties + +[cols="2,2,2,1,3"] +|=== +|Configuration Key |Equivalent Parameter |Environment Variable |Data Type |Purpose + +| `service.pingfederate.adminAPIPath` | `--pingfederate-admin-api-path` | PINGCLI_PINGFEDERATE_ADMIN_API_PATH | String | The PingFederate API URL path used to communicate with PingFederate's admin API. (default /pf-admin-api/v1) +| `service.pingfederate.authentication.accessTokenAuth.accessToken` | `--pingfederate-access-token` | PINGCLI_PINGFEDERATE_ACCESS_TOKEN | String | The PingFederate access token used to authenticate to the PingFederate admin API when using a custom OAuth 2.0 token method. +| `service.pingfederate.authentication.basicAuth.password` | `--pingfederate-password` | PINGCLI_PINGFEDERATE_PASSWORD | String | The PingFederate password used to authenticate to the PingFederate admin API when using basic authentication. +| `service.pingfederate.authentication.basicAuth.username` | `--pingfederate-username` | PINGCLI_PINGFEDERATE_USERNAME | String | The PingFederate username used to authenticate to the PingFederate admin API when using basic authentication. Example: 'administrator' +| `service.pingfederate.authentication.clientCredentialsAuth.clientID` | `--pingfederate-client-id` | PINGCLI_PINGFEDERATE_CLIENT_ID | String | The PingFederate OAuth client ID used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. +| `service.pingfederate.authentication.clientCredentialsAuth.clientSecret` | `--pingfederate-client-secret` | PINGCLI_PINGFEDERATE_CLIENT_SECRET | String | The PingFederate OAuth client secret used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. +| `service.pingfederate.authentication.clientCredentialsAuth.scopes` | `--pingfederate-scopes` | PINGCLI_PINGFEDERATE_SCOPES | String Array | The PingFederate OAuth scopes used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. (default []) Accepts a comma-separated string to delimit multiple scopes. Example: 'openid,profile' +| `service.pingfederate.authentication.clientCredentialsAuth.tokenURL` | `--pingfederate-token-url` | PINGCLI_PINGFEDERATE_TOKEN_URL | String | The PingFederate OAuth token URL used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. +| `service.pingfederate.authentication.type` | `--pingfederate-authentication-type` | PINGCLI_PINGFEDERATE_AUTHENTICATION_TYPE | String (Enum) | The authentication type to use when connecting to the PingFederate admin API. Options are: accessTokenAuth, basicAuth, clientCredentialsAuth. Example: 'basicAuth' +| `service.pingfederate.caCertificatePemFiles` | `--pingfederate-ca-certificate-pem-files` | PINGCLI_PINGFEDERATE_CA_CERTIFICATE_PEM_FILES | String Array | Relative or full paths to PEM-encoded certificate files to be trusted as root CAs when connecting to the PingFederate server over HTTPS. (default []) Accepts a comma-separated string to delimit multiple PEM files. +| `service.pingfederate.httpsHost` | `--pingfederate-https-host` | PINGCLI_PINGFEDERATE_HTTPS_HOST | String | The PingFederate HTTPS host used to communicate with PingFederate's admin API. Example: 'https://pingfederate-admin.bxretail.org' +| `service.pingfederate.insecureTrustAllTLS` | `--pingfederate-insecure-trust-all-tls` | PINGCLI_PINGFEDERATE_INSECURE_TRUST_ALL_TLS | Boolean | Trust any certificate when connecting to the PingFederate server admin API. (default false) This is insecure and shouldn't be enabled outside of testing. +| `service.pingfederate.xBypassExternalValidationHeader` | `--pingfederate-x-bypass-external-validation-header` | PINGCLI_PINGFEDERATE_X_BYPASS_EXTERNAL_VALIDATION_HEADER | Boolean | Bypass connection tests when configuring PingFederate (the X-BypassExternalValidation header when using PingFederate's admin API). (default false) +| `service.pingone.authentication.type` | `--pingone-authentication-type` | PINGCLI_PINGONE_AUTHENTICATION_TYPE | String (Enum) | The authentication type to use to authenticate to the PingOne management API. (default worker) Options are: worker. +| `service.pingone.authentication.worker.clientID` | `--pingone-worker-client-id` | PINGCLI_PINGONE_WORKER_CLIENT_ID | String (UUID Format) | The worker client ID used to authenticate to the PingOne management API. +| `service.pingone.authentication.worker.clientSecret` | `--pingone-worker-client-secret` | PINGCLI_PINGONE_WORKER_CLIENT_SECRET | String | The worker client secret used to authenticate to the PingOne management API. +| `service.pingone.authentication.worker.environmentID` | `--pingone-worker-environment-id` | PINGCLI_PINGONE_WORKER_ENVIRONMENT_ID | String (UUID Format) | The ID of the PingOne environment that contains the worker client used to authenticate to the PingOne management API. +| `service.pingone.regionCode` | `--pingone-region-code` | PINGCLI_PINGONE_REGION_CODE | String (Enum) | The region code of the PingOne tenant. Options are: AP, AU, CA, EU, NA. Example: 'NA' +|=== + +== Platform Export Properties + +[cols="2,2,2,1,3"] +|=== +|Configuration Key |Equivalent Parameter |Environment Variable |Data Type |Purpose + +| `export.format` | `--format` / `-f` | PINGCLI_EXPORT_FORMAT | String (Enum) | Specifies the export format. (default HCL) Options are: HCL. +| `export.outputDirectory` | `--output-directory` / `-d` | PINGCLI_EXPORT_OUTPUT_DIRECTORY | String | Specifies the output directory for export. Can be an absolute filepath or a relative filepath of the present working directory. Example: '/Users/example/pingcli-export' Example: 'pingcli-export' +| `export.overwrite` | `--overwrite` / `-o` | PINGCLI_EXPORT_OVERWRITE | Boolean | Overwrites the existing generated exports in output directory. (default false) +| `export.pingone.environmentID` | `--pingone-export-environment-id` | PINGCLI_PINGONE_EXPORT_ENVIRONMENT_ID | String (UUID Format) | The ID of the PingOne environment to export. Must be a valid PingOne UUID. +| `export.serviceGroup` | `--service-group` / `-g` | PINGCLI_EXPORT_SERVICE_GROUP | String (Enum) | Specifies the service group to export. Options are: pingone. Example: 'pingone' +| `export.services` | `--services` / `-s` | PINGCLI_EXPORT_SERVICES | String Array | Specifies the service(s) to export. Accepts a comma-separated string to delimit multiple services. Options are: pingfederate, pingone-authorize, pingone-mfa, pingone-platform, pingone-protect, pingone-sso. Example: 'pingone-sso,pingone-mfa,pingfederate' +|=== + +== License Properties + +[cols="2,2,2,1,3"] +|=== +|Configuration Key |Equivalent Parameter |Environment Variable |Data Type |Purpose + +| `license.devopsKey` | `--devops-key` / `-k` | PINGCLI_LICENSE_DEVOPS_KEY | String | The DevOps key for the license request. See https://developer.pingidentity.com/devops/how-to/devopsRegistration.html on how to register a DevOps user. You can save the DevOps user and key in your profile using the 'pingcli config' commands. +| `license.devopsUser` | `--devops-user` / `-u` | PINGCLI_LICENSE_DEVOPS_USER | String | The DevOps user for the license request. See https://developer.pingidentity.com/devops/how-to/devopsRegistration.html on how to register a DevOps user. You can save the DevOps user and key in your profile using the 'pingcli config' commands. +|=== + +== Custom Request Properties + +[cols="2,2,2,1,3"] +|=== +|Configuration Key |Equivalent Parameter |Environment Variable |Data Type |Purpose + +| `request.accessToken` | | | String | +| `request.accessTokenExpiry` | | | Integer | +| `request.fail` | `--fail` / `-f` | | Boolean | Return non-zero exit code when HTTP custom request returns a failure status code. +| `request.service` | `--service` / `-s` | PINGCLI_REQUEST_SERVICE | String (Enum) | The Ping service (configured in the active profile) to send the custom request to. Options are: pingone. Example: 'pingone' +|=== + diff --git a/docs/dev-ux-portal-docs/nav.adoc b/docs/dev-ux-portal-docs/nav.adoc new file mode 100644 index 00000000..4d0e1cc1 --- /dev/null +++ b/docs/dev-ux-portal-docs/nav.adoc @@ -0,0 +1,23 @@ +* Command Reference +** xref:command_reference:pingcli.adoc[] +** xref:command_reference:pingcli_completion.adoc[] +** xref:command_reference:pingcli_config.adoc[] +*** xref:command_reference:pingcli_config_add-profile.adoc[] +*** xref:command_reference:pingcli_config_delete-profile.adoc[] +*** xref:command_reference:pingcli_config_get.adoc[] +*** xref:command_reference:pingcli_config_list-keys.adoc[] +*** xref:command_reference:pingcli_config_list-profiles.adoc[] +*** xref:command_reference:pingcli_config_set.adoc[] +*** xref:command_reference:pingcli_config_set-active-profile.adoc[] +*** xref:command_reference:pingcli_config_unset.adoc[] +*** xref:command_reference:pingcli_config_view-profile.adoc[] +** xref:command_reference:pingcli_feedback.adoc[] +** xref:command_reference:pingcli_license.adoc[] +** xref:command_reference:pingcli_platform.adoc[] +*** xref:command_reference:pingcli_platform_export.adoc[] +** xref:command_reference:pingcli_plugin.adoc[] +*** xref:command_reference:pingcli_plugin_add.adoc[] +*** xref:command_reference:pingcli_plugin_list.adoc[] +*** xref:command_reference:pingcli_plugin_remove.adoc[] +** xref:command_reference:pingcli_request.adoc[] + diff --git a/docs/dev-ux-portal-docs/pingcli.adoc b/docs/dev-ux-portal-docs/pingcli.adoc new file mode 100644 index 00000000..637ae437 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli.adoc @@ -0,0 +1,36 @@ += pingcli +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli + +A CLI tool for managing the configuration of Ping Identity products. + +== Synopsis + +A CLI tool for managing the configuration of Ping Identity products. + +---- +pingcli +---- + +== Options + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -h, --help help for pingcli + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== Subcommands + +* xref:pingcli_completion.adoc[] - Prints shell completion scripts +* xref:pingcli_config.adoc[] - Manage the CLI configuration. +* xref:pingcli_feedback.adoc[] - Help us improve the CLI. Report issues or send us feedback on using the CLI tool. +* xref:pingcli_license.adoc[] - Request a new evaluation license. +* xref:pingcli_platform.adoc[] - Administer and manage the Ping integrated platform. +* xref:pingcli_plugin.adoc[] - Manage Ping CLI plugins. +* xref:pingcli_request.adoc[] - Send a custom REST API request to a Ping platform service. + diff --git a/docs/dev-ux-portal-docs/pingcli_completion.adoc b/docs/dev-ux-portal-docs/pingcli_completion.adoc new file mode 100644 index 00000000..d86bc238 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_completion.adoc @@ -0,0 +1,66 @@ += pingcli completion +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_completion + +Prints shell completion scripts + +== Synopsis + +To load completions: + +Bash: + + $ source <(pingcli completion bash) + + # To load completions for each session, execute once: + # Linux: + $ pingcli completion bash > /etc/bash_completion.d/pingcli + # macOS: + $ source <(pingcli completion zsh) + +Zsh: + + # If shell completion is not already enabled in your environment, + # you will need to enable it. You can execute the following once: + + $ echo "autoload -U compinit; compinit" >> ~/.zshrc + + # To load completions for each session, execute once: + $ pingcli completion zsh > "${fpath[1]}/_pingcli" + + # You will need to start a new shell for this setup to take effect. + +fish: + + $ pingcli completion fish | source + + # To load completions for each session, execute once: + $ pingcli completion fish > ~/.config/fish/completions/pingcli.fish + +PowerShell: + + PS> pingcli completion powershell | Out-String | Invoke-Expression + + # To load completions for every new session, run: + PS> pingcli completion powershell > pingcli.ps1 + # and source this file from your PowerShell profile. + +---- +pingcli completion [SHELL] +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli.adoc[] - A CLI tool for managing the configuration of Ping Identity products. + diff --git a/docs/dev-ux-portal-docs/pingcli_config.adoc b/docs/dev-ux-portal-docs/pingcli_config.adoc new file mode 100644 index 00000000..6056d051 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_config.adoc @@ -0,0 +1,56 @@ += pingcli config +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_config + +Manage the CLI configuration. + +== Synopsis + +Manage the configuration of the CLI, including Ping product connection parameters. + +The Ping CLI supports the use of configuration profiles. +Configuration profiles can be used when connecting to multiple environments using the same Ping CLI instance, such as when managing multiple development or demonstration environments. + +A pre-defined default profile will be used to store the configuration of the CLI. +Additional custom profiles can be created using the `pingcli config add-profile` command. +To use a custom profile, the `--profile` flag can be used on supported commands to specify the profile to use for that command. +To set a custom profile as the default, use the `pingcli config set-active-profile` command. + +---- +pingcli config +---- + +== Options + +---- + -U, --unmask-values Unmask secret values. (default false) + -h, --help help for config +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli.adoc[] - A CLI tool for managing the configuration of Ping Identity products. + +== Subcommands + +* xref:pingcli_config_add-profile.adoc[] - Add a new custom configuration profile. +* xref:pingcli_config_delete-profile.adoc[] - Delete a custom configuration profile. +* xref:pingcli_config_get.adoc[] - Read stored configuration settings for the CLI. +* xref:pingcli_config_list-keys.adoc[] - List all configuration keys. +* xref:pingcli_config_list-profiles.adoc[] - List all custom configuration profiles. +* xref:pingcli_config_set.adoc[] - Set stored configuration settings for the CLI. +* xref:pingcli_config_set-active-profile.adoc[] - Set a custom configuration profile as the in-use profile. +* xref:pingcli_config_unset.adoc[] - Unset stored configuration settings for the CLI. +* xref:pingcli_config_view-profile.adoc[] - View the stored configuration of a custom configuration profile. + diff --git a/docs/dev-ux-portal-docs/pingcli_config_add-profile.adoc b/docs/dev-ux-portal-docs/pingcli_config_add-profile.adoc new file mode 100644 index 00000000..a5c6951f --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_config_add-profile.adoc @@ -0,0 +1,54 @@ += pingcli config add-profile +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_config_add-profile + +Add a new custom configuration profile. + +== Synopsis + +Add a new custom configuration profile to the CLI. + +The new configuration profile will be stored in the CLI configuration file. + +---- +pingcli config add-profile [flags] +---- + +== Examples + +---- + Add a new configuration profile with a guided experience. + pingcli config add-profile + + Add a new configuration profile with a specific name and description. + pingcli config add-profile --name myprofile --description "My awesome new profile for my development environment" + + Add a new configuration profile with a guided experience and set it as the active profile. + pingcli config add-profile --set-active +---- + +== Options + +---- + -d, --description string The description of the new configuration profile. + -h, --help help for add-profile + -n, --name string The name of the new configuration profile. + -s, --set-active Set the new configuration profile as the active profile. (default false) +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + -U, --unmask-values Unmask secret values. (default false) + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_config.adoc[] - Manage the CLI configuration. + diff --git a/docs/dev-ux-portal-docs/pingcli_config_delete-profile.adoc b/docs/dev-ux-portal-docs/pingcli_config_delete-profile.adoc new file mode 100644 index 00000000..fe0a4d8a --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_config_delete-profile.adoc @@ -0,0 +1,52 @@ += pingcli config delete-profile +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_config_delete-profile + +Delete a custom configuration profile. + +== Synopsis + +Delete an existing custom configuration profile from the CLI. + +The profile to delete will be removed from the CLI configuration file. + +---- +pingcli config delete-profile [flags] [profile-name] +---- + +== Examples + +---- + Delete a configuration profile by selecting from the available profiles. + pingcli config delete-profile + + Delete a configuration profile by specifying the name of an existing configured profile. + pingcli config delete-profile myprofile + + Delete a configuration profile by auto-accepting the deletion. + pingcli config delete-profile --yes myprofile +---- + +== Options + +---- + -y, --yes Auto-accept the profile deletion confirmation prompt. (default false) + -h, --help help for delete-profile +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + -U, --unmask-values Unmask secret values. (default false) + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_config.adoc[] - Manage the CLI configuration. + diff --git a/docs/dev-ux-portal-docs/pingcli_config_get.adoc b/docs/dev-ux-portal-docs/pingcli_config_get.adoc new file mode 100644 index 00000000..a6aa8520 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_config_get.adoc @@ -0,0 +1,49 @@ += pingcli config get +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_config_get + +Read stored configuration settings for the CLI. + +== Synopsis + +Read stored configuration settings for the CLI. + +The `--profile` parameter can be used to read configuration settings for a specified custom configuration profile. +Where `--profile` is not specified, configuration settings will be read for the currently active profile. + +---- +pingcli config get [flags] key +---- + +== Examples + +---- + Read all the configuration settings for the PingOne service in the active (or default) profile. + pingcli config get pingone + + Read the noColor setting for the profile named 'myprofile'. + pingcli config get --profile myprofile noColor + + Read the worker ID used to authenticate to the PingOne service management API. + pingcli config get service.pingOne.authentication.worker.environmentID + + Read the unmasked value for the request access token. + pingcli config get --unmask-values request.accessToken +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + -U, --unmask-values Unmask secret values. (default false) + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_config.adoc[] - Manage the CLI configuration. + diff --git a/docs/dev-ux-portal-docs/pingcli_config_list-keys.adoc b/docs/dev-ux-portal-docs/pingcli_config_list-keys.adoc new file mode 100644 index 00000000..de516c7a --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_config_list-keys.adoc @@ -0,0 +1,46 @@ += pingcli config list-keys +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_config_list-keys + +List all configuration keys. + +== Synopsis + +View the complete list of available configuration options. These attributes can be saved via the set and unset config subcommands or stored in a profile within the config file. +For details on the configuration options visit: https://github.com/pingidentity/pingcli/blob/main/docs/tool-configuration/configuration-key.md + +---- +pingcli config list-keys [flags] +---- + +== Examples + +---- + List all configuration keys stored in the CLI configuration file. + pingcli config list-keys + pingcli config list-keys --yaml +---- + +== Options + +---- + -y, --yaml Output configuration keys in YAML format. (default false) + -h, --help help for list-keys +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + -U, --unmask-values Unmask secret values. (default false) + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_config.adoc[] - Manage the CLI configuration. + diff --git a/docs/dev-ux-portal-docs/pingcli_config_list-profiles.adoc b/docs/dev-ux-portal-docs/pingcli_config_list-profiles.adoc new file mode 100644 index 00000000..42f02b9e --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_config_list-profiles.adoc @@ -0,0 +1,37 @@ += pingcli config list-profiles +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_config_list-profiles + +List all custom configuration profiles. + +== Synopsis + +List all custom configuration profiles stored in the CLI configuration file. + +---- +pingcli config list-profiles +---- + +== Examples + +---- + List all custom configuration profiles stored in the CLI configuration file. + pingcli config list-profiles +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + -U, --unmask-values Unmask secret values. (default false) + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_config.adoc[] - Manage the CLI configuration. + diff --git a/docs/dev-ux-portal-docs/pingcli_config_set-active-profile.adoc b/docs/dev-ux-portal-docs/pingcli_config_set-active-profile.adoc new file mode 100644 index 00000000..cb542738 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_config_set-active-profile.adoc @@ -0,0 +1,40 @@ += pingcli config set-active-profile +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_config_set-active-profile + +Set a custom configuration profile as the in-use profile. + +== Synopsis + +Set a custom configuration profile as the in-use profile. + +---- +pingcli config set-active-profile [flags] [profile-name] +---- + +== Examples + +---- + Set an active profile with an interactive prompt to select from an available profile. + pingcli config set-active-profile + + Set an active profile with a specific profile name. + pingcli config set-active-profile myprofile +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + -U, --unmask-values Unmask secret values. (default false) + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_config.adoc[] - Manage the CLI configuration. + diff --git a/docs/dev-ux-portal-docs/pingcli_config_set.adoc b/docs/dev-ux-portal-docs/pingcli_config_set.adoc new file mode 100644 index 00000000..77ac8f96 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_config_set.adoc @@ -0,0 +1,46 @@ += pingcli config set +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_config_set + +Set stored configuration settings for the CLI. + +== Synopsis + +Set stored configuration settings for the CLI. + +The `--profile` parameter can be used to set configuration settings for a specified custom configuration profile. +Where `--profile` is not specified, configuration settings will be set for the currently active profile. + +---- +pingcli config set [flags] key=value +---- + +== Examples + +---- + Set the color setting to true for the currently active profile. + pingcli config set color=true + + Set the PingOne tenant region code setting to 'AP' for the profile named 'myprofile'. + pingcli config set --profile myprofile service.pingOne.regionCode=AP + + Set the PingFederate basic authentication password with unmasked output + pingcli config set --profile myprofile --unmask-values service.pingFederate.authentication.basicAuth.password=1234 +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + -U, --unmask-values Unmask secret values. (default false) + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_config.adoc[] - Manage the CLI configuration. + diff --git a/docs/dev-ux-portal-docs/pingcli_config_unset.adoc b/docs/dev-ux-portal-docs/pingcli_config_unset.adoc new file mode 100644 index 00000000..a6f4538b --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_config_unset.adoc @@ -0,0 +1,43 @@ += pingcli config unset +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_config_unset + +Unset stored configuration settings for the CLI. + +== Synopsis + +Unset stored configuration settings for the CLI. + +The `--profile` parameter can be used to unset configuration settings for a specified custom configuration profile. +Where `--profile` is not specified, configuration settings will be unset for the currently active profile. + +---- +pingcli config unset [flags] key +---- + +== Examples + +---- + Unset the color setting for the currently active profile. + pingcli config unset color + + Unset the PingOne tenant region code setting for the profile named 'myprofile'. + pingcli config unset --profile myprofile service.pingOne.regionCode +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + -U, --unmask-values Unmask secret values. (default false) + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_config.adoc[] - Manage the CLI configuration. + diff --git a/docs/dev-ux-portal-docs/pingcli_config_view-profile.adoc b/docs/dev-ux-portal-docs/pingcli_config_view-profile.adoc new file mode 100644 index 00000000..62482bce --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_config_view-profile.adoc @@ -0,0 +1,43 @@ += pingcli config view-profile +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_config_view-profile + +View the stored configuration of a custom configuration profile. + +== Synopsis + +View the stored configuration of a custom configuration profile. + +---- +pingcli config view-profile [flags] [profile-name] +---- + +== Examples + +---- + View configuration for the currently active profile + pingcli config view-profile + + View configuration for a specific profile + pingcli config view-profile myprofile + + View configuration for a specific profile with unmasked values + pingcli config --unmask-values view-profile myprofile +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + -U, --unmask-values Unmask secret values. (default false) + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_config.adoc[] - Manage the CLI configuration. + diff --git a/docs/dev-ux-portal-docs/pingcli_feedback.adoc b/docs/dev-ux-portal-docs/pingcli_feedback.adoc new file mode 100644 index 00000000..28fd2c23 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_feedback.adoc @@ -0,0 +1,35 @@ += pingcli feedback +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_feedback + +Help us improve the CLI. Report issues or send us feedback on using the CLI tool. + +== Synopsis + +Provides links to report issues and provide feedback on using the CLI to Ping Identity. + +---- +pingcli feedback [flags] +---- + +== Examples + +---- + pingcli feedback +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli.adoc[] - A CLI tool for managing the configuration of Ping Identity products. + diff --git a/docs/dev-ux-portal-docs/pingcli_license.adoc b/docs/dev-ux-portal-docs/pingcli_license.adoc new file mode 100644 index 00000000..0010c4b5 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_license.adoc @@ -0,0 +1,51 @@ += pingcli license +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_license + +Request a new evaluation license. + +== Synopsis + +Request a new evaluation license for a specific product and version. + +The new license request will be sent to the Ping Identity license server. + +---- +pingcli license [flags] +---- + +== Examples + +---- + Request a new evaluation license for PingFederate 12.0. + pingcli license request --product pingfederate --version 12.0 + + Request a new evaluation license for PingAccess 6.3. + pingcli license request --product pingaccess --version 6.3 +---- + +== Options + +---- + -k, --devops-key string The DevOps key for the license request. See https://developer.pingidentity.com/devops/how-to/devopsRegistration.html on how to register a DevOps user. You can save the DevOps user and key in your profile using the 'pingcli config' commands. + -h, --help help for license + -p, --product string The product for which to request a license. Options are: pingaccess, pingauthorize, pingauthorize-policy-editor, pingcentral, pingdirectory, pingdirectoryproxy, pingfederate. Example: 'pingfederate' + -u, --devops-user string The DevOps user for the license request. See https://developer.pingidentity.com/devops/how-to/devopsRegistration.html on how to register a DevOps user. You can save the DevOps user and key in your profile using the 'pingcli config' commands. + -v, --version string The version of the product for which to request a license. Must be of the form 'major.minor'. Example: '12.3' +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli.adoc[] - A CLI tool for managing the configuration of Ping Identity products. + diff --git a/docs/dev-ux-portal-docs/pingcli_platform.adoc b/docs/dev-ux-portal-docs/pingcli_platform.adoc new file mode 100644 index 00000000..4b9dbc56 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_platform.adoc @@ -0,0 +1,37 @@ += pingcli platform +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_platform + +Administer and manage the Ping integrated platform. + +== Synopsis + +Administer and manage the Ping integrated platform. + +When multiple products are configured in the CLI, the platform command can be used to manage one or more products collectively. + +The --profile command switch can be used to specify the profile of Ping products to be managed. + +---- +pingcli platform +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli.adoc[] - A CLI tool for managing the configuration of Ping Identity products. + +== Subcommands + +* xref:pingcli_platform_export.adoc[] - Export Configuration as Code packages for the Ping Platform. + diff --git a/docs/dev-ux-portal-docs/pingcli_platform_export.adoc b/docs/dev-ux-portal-docs/pingcli_platform_export.adoc new file mode 100644 index 00000000..f8b19ca5 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_platform_export.adoc @@ -0,0 +1,95 @@ += pingcli platform export +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_platform_export + +Export Configuration as Code packages for the Ping Platform. + +== Synopsis + +Export Configuration as Code packages for the Ping Platform. + +The CLI can export Terraform HCL to use with released Terraform providers. +The Terraform HCL option generates `import {}` block statements for resources in the target environment. +Using Terraform `import {}` blocks, the platform's configuration can be generated and imported into state management. +More information can be found at https://developer.hashicorp.com/terraform/language/import + +---- +pingcli platform export [flags] +---- + +== Examples + +---- + Export Configuration as Code for all products configured in the configuration file, applying default options. + pingcli platform export + + Export Configuration as Code packages for all configured products to a specific directory, overwriting any previous export. + pingcli platform export --output-directory /path/to/my/directory --overwrite + + Export Configuration as Code packages for all configured products, specifying the export format as Terraform HCL. + pingcli platform export --format HCL + + Export Configuration as Code packages for PingOne (core platform and SSO services). + pingcli platform export --services pingone-platform,pingone-sso + + Export all Configuration as Code packages for PingOne. The --service-group flag can be used instead of listing all pingone-* packages in --services flag. + pingcli platform export --service-group pingone + + Export Configuration as Code packages for PingOne (core platform), specifying the PingOne environment connection details. + pingcli platform export --services pingone-platform --pingone-client-environment-id 3cf2... --pingone-worker-client-id a719... --pingone-worker-client-secret ey..... --pingone-region-code EU + + Export Configuration as Code packages for PingFederate, specifying the PingFederate connection details using basic authentication. + pingcli platform export --services pingfederate --pingfederate-authentication-type basicAuth --pingfederate-username administrator --pingfederate-password 2FederateM0re --pingfederate-https-host https://pingfederate-admin.bxretail.org + + Export Configuration as Code packages for PingFederate, specifying the PingFederate connection details using OAuth 2.0 client credentials. + pingcli platform export --services pingfederate --pingfederate-authentication-type clientCredentialsAuth --pingfederate-client-id clientID --pingfederate-client-secret clientSecret --pingfederate-token-url https://pingfederate-admin.bxretail.org/as/token.oauth2 + + Export Configuration as Code packages for PingFederate, specifying optional connection properties + pingcli platform export --services pingfederate --x-bypass-external-validation=false --ca-certificate-pem-files "/path/to/cert.pem,/path/to/cert2.pem" --insecure-trust-all-tls=false +---- + +== Options + +---- + -d, --output-directory string Specifies the output directory for export. Can be an absolute filepath or a relative filepath of the present working directory. Example: '/Users/example/pingcli-export' Example: 'pingcli-export' + -h, --help help for export + -f, --format string Specifies the export format. (default HCL) Options are: HCL. + -g, --service-group string Specifies the service group to export. Options are: pingone. Example: 'pingone' + -o, --overwrite Overwrites the existing generated exports in output directory. (default false) + -s, --services []string Specifies the service(s) to export. Accepts a comma-separated string to delimit multiple services. Options are: pingfederate, pingone-authorize, pingone-mfa, pingone-platform, pingone-protect, pingone-sso. Example: 'pingone-sso,pingone-mfa,pingfederate' + --pingfederate-access-token string The PingFederate access token used to authenticate to the PingFederate admin API when using a custom OAuth 2.0 token method. + --pingfederate-admin-api-path string The PingFederate API URL path used to communicate with PingFederate's admin API. (default /pf-admin-api/v1) + --pingfederate-authentication-type string The authentication type to use when connecting to the PingFederate admin API. Options are: accessTokenAuth, basicAuth, clientCredentialsAuth. Example: 'basicAuth' + --pingfederate-ca-certificate-pem-files []string Relative or full paths to PEM-encoded certificate files to be trusted as root CAs when connecting to the PingFederate server over HTTPS. (default []) Accepts a comma-separated string to delimit multiple PEM files. + --pingfederate-client-id string The PingFederate OAuth client ID used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. + --pingfederate-client-secret string The PingFederate OAuth client secret used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. + --pingfederate-https-host string The PingFederate HTTPS host used to communicate with PingFederate's admin API. Example: 'https://pingfederate-admin.bxretail.org' + --pingfederate-insecure-trust-all-tls Trust any certificate when connecting to the PingFederate server admin API. (default false) This is insecure and shouldn't be enabled outside of testing. + --pingfederate-password string The PingFederate password used to authenticate to the PingFederate admin API when using basic authentication. + --pingfederate-scopes []string The PingFederate OAuth scopes used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. (default []) Accepts a comma-separated string to delimit multiple scopes. Example: 'openid,profile' + --pingfederate-token-url string The PingFederate OAuth token URL used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. + --pingfederate-username string The PingFederate username used to authenticate to the PingFederate admin API when using basic authentication. Example: 'administrator' + --pingfederate-x-bypass-external-validation-header Bypass connection tests when configuring PingFederate (the X-BypassExternalValidation header when using PingFederate's admin API). (default false) + --pingone-authentication-type string The authentication type to use to authenticate to the PingOne management API. (default worker) Options are: worker. + --pingone-export-environment-id string The ID of the PingOne environment to export. Must be a valid PingOne UUID. + --pingone-region-code string The region code of the PingOne tenant. Options are: AP, AU, CA, EU, NA. Example: 'NA' + --pingone-worker-client-id string The worker client ID used to authenticate to the PingOne management API. + --pingone-worker-client-secret string The worker client secret used to authenticate to the PingOne management API. + --pingone-worker-environment-id string The ID of the PingOne environment that contains the worker client used to authenticate to the PingOne management API. +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_platform.adoc[] - Administer and manage the Ping integrated platform. + diff --git a/docs/dev-ux-portal-docs/pingcli_plugin.adoc b/docs/dev-ux-portal-docs/pingcli_plugin.adoc new file mode 100644 index 00000000..47e6f1ae --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_plugin.adoc @@ -0,0 +1,35 @@ += pingcli plugin +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_plugin + +Manage Ping CLI plugins. + +== Synopsis + +Manage Ping CLI plugins. + +---- +pingcli plugin +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli.adoc[] - A CLI tool for managing the configuration of Ping Identity products. + +== Subcommands + +* xref:pingcli_plugin_add.adoc[] - Add a plugin to use with Ping CLI +* xref:pingcli_plugin_list.adoc[] - List all plugins currently in use with Ping CLI +* xref:pingcli_plugin_remove.adoc[] - Remove a plugin from Ping CLI + diff --git a/docs/dev-ux-portal-docs/pingcli_plugin_add.adoc b/docs/dev-ux-portal-docs/pingcli_plugin_add.adoc new file mode 100644 index 00000000..4e10c7d9 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_plugin_add.adoc @@ -0,0 +1,36 @@ += pingcli plugin add +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_plugin_add + +Add a plugin to use with Ping CLI + +== Synopsis + +Add a plugin to use with Ping CLI. + +---- +pingcli plugin add plugin-executable +---- + +== Examples + +---- + Add a plugin to use with Ping CLI. + pingcli plugin add pingcli-plugin-executable +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_plugin.adoc[] - Manage Ping CLI plugins. + diff --git a/docs/dev-ux-portal-docs/pingcli_plugin_list.adoc b/docs/dev-ux-portal-docs/pingcli_plugin_list.adoc new file mode 100644 index 00000000..481ef8af --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_plugin_list.adoc @@ -0,0 +1,36 @@ += pingcli plugin list +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_plugin_list + +List all plugins currently in use with Ping CLI + +== Synopsis + +List all plugins currently in use with Ping CLI. + +---- +pingcli plugin list +---- + +== Examples + +---- + List all plugins currently in use with Ping CLI. + pingcli plugin list +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_plugin.adoc[] - Manage Ping CLI plugins. + diff --git a/docs/dev-ux-portal-docs/pingcli_plugin_remove.adoc b/docs/dev-ux-portal-docs/pingcli_plugin_remove.adoc new file mode 100644 index 00000000..a47735df --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_plugin_remove.adoc @@ -0,0 +1,36 @@ += pingcli plugin remove +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_plugin_remove + +Remove a plugin from Ping CLI + +== Synopsis + +Remove a plugin from Ping CLI. + +---- +pingcli plugin remove plugin-executable +---- + +== Examples + +---- + Remove a plugin from Ping CLI. + pingcli plugin remove pingcli-plugin-executable +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli_plugin.adoc[] - Manage Ping CLI plugins. + diff --git a/docs/dev-ux-portal-docs/pingcli_request.adoc b/docs/dev-ux-portal-docs/pingcli_request.adoc new file mode 100644 index 00000000..3e2849d2 --- /dev/null +++ b/docs/dev-ux-portal-docs/pingcli_request.adoc @@ -0,0 +1,66 @@ += pingcli request +:created-date: September 18, 2025 +:revdate: September 18, 2025 +:resourceid: pingcli_command_reference_pingcli_request + +Send a custom REST API request to a Ping platform service. + +== Synopsis + +Send a custom REST API request to a Ping Service. + +The custom REST API request is most powerful when product connection details have been configured in the CLI configuration file. +The command offers a cURL-like experience to interact with the Ping platform services, with authentication and environment details dynamically filled by the CLI. + +---- +pingcli request [flags] API_URI +---- + +== Examples + +---- + Send a custom API request to the configured PingOne tenant, making a GET request against the /environments endpoint. + pingcli request --service pingone environments + + Send a custom API request to the configured PingOne tenant, making a GET request to retrieve JSON configuration for a specific environment. + pingcli request --service pingone --http-method GET --output-format json environments/$MY_ENVIRONMENT_ID + + Send a custom API request to the configured PingOne tenant, making a POST request to create a new environment with JSON data sourced from a file. + pingcli request --service pingone --http-method POST --data ./my-environment.json environments + + Send a custom API request to the configured PingOne tenant, making a POST request using a custom header to create users with JSON data sourced from a file. + pingcli request --service pingone --http-method POST --header "Content-Type: application/vnd.pingidentity.user.import+json" --data ./users.json environments/$MY_ENVIRONMENT_ID/users + + Send a custom API request to the configured PingOne tenant, making a POST request to create a new environment using raw JSON data. + pingcli request --service pingone --http-method POST --data-raw '{"name": "My environment"}' environments + + Send a custom API request to the configured PingOne tenant, making a DELETE request to remove an application attribute mapping. + pingcli request --service pingone --http-method DELETE environments/$MY_ENVIRONMENT_ID/applications/$MY_APPLICATION_ID/attributes/$MY_ATTRIBUTE_MAPPING_ID +---- + +== Options + +---- + -f, --fail Return non-zero exit code when HTTP custom request returns a failure status code. + -h, --help help for request + -m, --http-method string The HTTP method to use for the request. (default GET) Options are: DELETE, GET, PATCH, POST, PUT. Example: 'POST' + -r, --header []string A custom header to send in the request. Example: --header "Content-Type: application/vnd.pingidentity.user.import+json" + -s, --service string The Ping service (configured in the active profile) to send the custom request to. Options are: pingone. Example: 'pingone' + --data string The file containing data to send in the request. Example: './data.json' + --data-raw string The raw data to send in the request. Example: '{"name": "My environment"}' +---- + +== Options inherited from parent commands + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== More information + +* xref:pingcli.adoc[] - A CLI tool for managing the configuration of Ping Identity products. + diff --git a/tools/README_DocumentGeneration.md b/tools/README_DocumentGeneration.md new file mode 100644 index 00000000..0c0919b3 --- /dev/null +++ b/tools/README_DocumentGeneration.md @@ -0,0 +1,147 @@ +# Documentation Generation (Configuration Options & Command Reference) + +This document explains how to generate all Ping CLI documentation artifacts, how the golden +tests validate output, and the available Makefile targets & direct `go run` equivalents. + +## Overview + +There are two primary documentation generators: + +1. Configuration Options Reference (`tools/generate-options-docs`) +2. Command Reference Pages + Navigation (`tools/generate-command-docs`) + +Both tools produce AsciiDoc that is ingested by the documentation portal. Golden tests +run automatically (via the Makefile targets) to ensure formatting changes are intentional. + +## Configuration Options Documentation + +Generate the configuration options reference (default output path is +`docs/dev-ux-portal-docs/general/cli-configuration-settings-reference.adoc`): + +```shell +make generate-options-docs +``` + +Override the output using the `OUTPUT` variable (the argument you pass to `OUTPUT` is +forwarded directly to the generator): + +```shell +make generate-options-docs OUTPUT='-o docs/options.md' +make generate-options-docs OUTPUT='-o docs/options.adoc' +``` + +When called through the Makefile without `OUTPUT`, AsciiDoc is written to the portal path. +When you invoke the generator directly without `-o`, output is written to stdout. + +Direct invocation examples: + +```shell +go run ./tools/generate-options-docs -o docs/options.md +go run ./tools/generate-options-docs -o docs/dev-ux-portal-docs/general/cli-configuration-settings-reference.adoc +go run ./tools/generate-options-docs -asciidoc > docs/dev-ux-portal-docs/general/cli-configuration-settings-reference.adoc +``` + +The AsciiDoc generator orders sections as: General, Service, Export, License, Request (as of September 2025 - new options may change this order). + +Data types: If a Data Type cell shows "N/A", it means the option's data type hasn't been mapped in the generator yet. Review and add a mapping in `tools/generate-options-docs/docgen/docgen.go` (see `asciiDocDataType`). This is intentional to surface new or unknown types for review rather than silently defaulting. + +## Command Reference Pages & Navigation + +Generate a page for every command and subcommand plus a hierarchical navigation file +(`nav.adoc`) suitable for portal ingestion: + +```shell +make generate-command-docs +``` + +The generator writes per-command `.adoc` files and `nav.adoc` into `docs/dev-ux-portal-docs`. +Each page includes AsciiDoc attributes: + +```adoc +:created-date: +:revdate: +:resourceid: +``` + +These values appear immediately under the document title. `nav.adoc` is always regenerated; +manual edits will be overwritten. + +Direct invocation: + +```shell +go run ./tools/generate-command-docs -o ./docs/dev-ux-portal-docs +``` + +Override the date used in the page headers (for reproducible builds) with: + +```shell +go run ./tools/generate-command-docs -date "March 23, 2025" -o ./docs/dev-ux-portal-docs +``` + +## Rebuilding All Documentation + +Force a clean rebuild (removes `docs/dev-ux-portal-docs` then regenerates both sets): + +```shell +make generate-all-docs +``` + +Sequence executed: + +1. Remove existing `docs/dev-ux-portal-docs` +2. Generate configuration options reference +3. Generate all command pages + `nav.adoc` +4. Run golden tests (each generator target runs its own test suite) + +Equivalent (manual) direct runs: + +```shell +go run ./tools/generate-options-docs -o docs/dev-ux-portal-docs/general/cli-configuration-settings-reference.adoc +go run ./tools/generate-command-docs -o docs/dev-ux-portal-docs +``` + +## Golden Tests Integration + +Golden tests live alongside each generator: + +- `tools/generate-options-docs/docgen/docgen_test.go` +- `tools/generate-command-docs/main_test.go` + +They compare current output against committed fixtures. Dynamic lines (`:created-date:` and +`:revdate:`) are stripped before comparison to keep goldens stable. + +To intentionally update goldens (for formatting changes): + +```shell +go test ./tools/generate-options-docs/docgen -run TestOptionsDocGeneration -update +go test ./tools/generate-command-docs -run TestCommandDocGeneration -update +``` + +Running the Makefile targets automatically executes the associated golden tests: + +```shell +make generate-options-docs +make generate-command-docs +make generate-all-docs +``` + +## Makefile Targets Summary + +| Target | Purpose | +|--------|---------| +| `generate-options-docs` | Generate configuration options reference (AsciiDoc by default) + run golden test | +| `generate-command-docs` | Generate per-command pages + navigation + run golden test | +| `generate-all-docs` | Clean and rebuild both sets (runs both golden tests) | + +## Troubleshooting + +| Issue | Resolution | +|-------|------------| +| Golden test fails after code change | Run with `-update` to refresh fixtures if changes are intentional | +| Navigation missing root command | Ensure `renderNav` includes the root (already implemented) | +| Dates cause diffs | They are normalized in tests; ensure you did not alter attribute names | +| "N/A" appears in Data Type column | The option type is not mapped in the generator. Update `asciiDocDataType` in `tools/generate-options-docs/docgen/docgen.go` (or introduce a new `options.Type` as appropriate). | + +## See Also + +Main project README: `../README.md` diff --git a/tools/README_DocumentationUpdatePortal.md b/tools/README_DocumentationUpdatePortal.md new file mode 100644 index 00000000..83abf32b --- /dev/null +++ b/tools/README_DocumentationUpdatePortal.md @@ -0,0 +1,40 @@ +# Documentation Portal Update Instructions + +This document explains how to use the generated documentation artifacts to update the Ping CLI documentation portal. The goal for future use is automation, but for now this is a manual process. + +Follow these steps after generating the documentation artifacts as described in +`README_DocumentGeneration.md`. + +## Overview + +There are three primary document types produced by the generators: + +1. nav.adoc (command hierarchy navigation snippet) +2. Command reference pages (one per command/subcommand) +3. Configuration options reference (single page) + +The nav.adoc is a code snippet to be inserted into the portal's `nav.adoc` file. +The command reference pages and configuration options reference are full AsciiDoc documents suitable for direct inclusion in the portal and can be simply copied over to the appropriate locations. + +## Process + +### nav.adoc + +Copy the contents of `docs/dev-ux-portal-docs/nav.adoc` and paste it into the +portal's `nav.adoc` file, replacing the content that starts with `* Command Reference`. +This section will need to be replaced each time the command docs are regenerated as new subcommands are added. + +The target file location in the portal repo is:`asciidoc/modules/ROOT/nav.adoc` + +### Command Reference Pages + +Copy the remaining `.adoc` files from `docs/dev-ux-portal-docs/` to the folder +`asciidoc/modules/command_reference\pages` of the documentation portal repository. +You can overwrite existing files and add any new ones. + +### Configuration Options Reference + +Copy the file `docs/dev-ux-portal-docs/general/cli-configuration-settings-reference.adoc` +to the folder `asciidoc/modules/ROOT/pages/general` of the documentation portal repository, overwriting the existing file of the same name. + +From this point, you can follow the process for building, reviewing, and publishing the portal documentation as outlined internally. diff --git a/tools/check-duplicates/main.go b/tools/check-duplicates/main.go new file mode 100644 index 00000000..6319d3b2 --- /dev/null +++ b/tools/check-duplicates/main.go @@ -0,0 +1,205 @@ +package main + +// duplicate function body detector +// --------------------------------- +// This small utility scans selected Go source directories and reports functions whose +// bodies are structurally identical. It's intended to help spot accidental copy/paste +// duplication (especially when refactoring small helper functions in tools or +// configuration option handling). +// +// HOW IT WORKS +// 1. Walk a curated set of directories (see includeDirs) collecting .go files (excluding tests). +// 2. Parse each file with the Go parser into an AST. +// 3. For every function with a body, iterate each statement node and build a textual +// representation using the %#v (Go-syntax) formatting of the AST nodes. +// 4. Normalize this textual representation (case fold, trim extra whitespace, remove newlines) +// to reduce noise (e.g., formatting differences) while still being stable. +// 5. Hash (SHA‑256) the normalized body representation. The hash becomes a key in a map +// to the list of (file:function) locations that share that exact body hash. +// 6. Any hash with more than one location is reported as a duplicate. +// +// WHY NORMALIZE? +// The AST %#v output can vary in insignificant whitespace. Normalization reduces false +// negatives from formatting differences but still treats any token / structural change as different. +// +// LIMITATIONS / NON-GOALS +// * Ignores function signatures (we only compare bodies). Two functions with different +// names/parameters but identical logic are flagged—which is desired for dedupe. +// * Does not attempt near-duplicate detection (e.g., only one constant differs). +// * Anonymous functions (lambdas) are ignored because we only traverse top-level *ast.FuncDecl. +// * Methods vs functions: receiver differences are ignored (body only). +// +// EXIT CODES +// 0 = No duplicates found +// 2 = One or more duplicate pairs reported +// 1 = I/O or parsing failure during traversal +// +// TYPICAL USAGE +// go run ./tools/check-duplicates +// or as a CI guard / Makefile target. +// +// To extend scanning, add paths to includeDirs. Keep the list tight to avoid noisy matches +// across unrelated packages. + +import ( + "bytes" + "crypto/sha256" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io" + "io/fs" + "os" + "path/filepath" + "regexp" + "strings" +) + +// includeDirs restricts the scan to a safe subset of the repository. Adjust cautiously. +var includeDirs = []string{ + "tools", + "internal/configuration/options", +} + +// ignoreFiles filters out generated or undesirable files (currently: test sources). +var ignoreFiles = regexp.MustCompile(`_test\.go$`) + +func main() { + // Map: bodyHash -> list of locations (file:functionName) + funcMap := map[string][]string{} + // Collect non-fatal per-file errors (I/O, parse) to report after traversal. + var errs []string + // Counters for summary + var filesScanned int + var funcsHashed int + err := filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() { + return err + } + if !strings.HasSuffix(path, ".go") || ignoreFiles.MatchString(path) { + return nil + } + if !withinIncluded(path) { + return nil + } + filesScanned++ + if n, e := addFile(path, funcMap); e != nil { + errs = append(errs, fmt.Sprintf("%s: %v", path, e)) + } else { + funcsHashed += n + } + + return nil + }) + if err != nil { + fmt.Fprintln(os.Stderr, "walk error:", err) + os.Exit(1) + } + + // Build list of every pair among colliding function bodies. + var collisions [][2]string + for _, locs := range funcMap { + if len(locs) > 1 { + for i := range locs { + for j := i + 1; j < len(locs); j++ { + collisions = append(collisions, [2]string{locs[i], locs[j]}) + } + } + } + } + + // Print summary and detailed output + fmt.Println("Summary:") + fmt.Printf(" Files scanned: %d\n", filesScanned) + fmt.Printf(" Functions hashed: %d\n", funcsHashed) + fmt.Printf(" Duplicate pairs: %d\n", len(collisions)) + fmt.Printf(" Errors: %d\n", len(errs)) + + if len(errs) > 0 { + fmt.Println("\nErrors during analysis:") + for _, e := range errs { + fmt.Println(" -", e) + } + os.Exit(1) + } + + if len(collisions) == 0 { + fmt.Println("\nNo duplicate functions found.") + + return + } + fmt.Println("\nDuplicate functions detected:") + for _, c := range collisions { + fmt.Printf(" - %s == %s\n", c[0], c[1]) + } + os.Exit(2) +} + +// withinIncluded returns true if the path is rooted in one of the includeDirs. +func withinIncluded(path string) bool { + for _, d := range includeDirs { + if strings.HasPrefix(path, d+"/") { + return true + } + } + + return false +} + +// addFile parses a Go file, hashes each function body, and records its location under that hash key. +// addFile returns the number of function bodies hashed from the file and any error encountered. +func addFile(path string, funcMap map[string][]string) (int, error) { + // Sanitize and restrict the path before opening (addresses gosec G304 false positive). + clean := filepath.Clean(path) + if filepath.IsAbs(clean) || strings.Contains(clean, "..") { + return 0, nil // reject unexpected absolute or parent traversals + } + if !withinIncluded(clean) || !strings.HasSuffix(clean, ".go") || ignoreFiles.MatchString(clean) { + return 0, nil + } + + f, err := os.Open(clean) // #nosec G304: path origin is controlled by WalkDir + allowlist + sanitization above + if err != nil { + return 0, fmt.Errorf("open: %w", err) + } + defer func() { _ = f.Close() }() + src, err := io.ReadAll(f) + if err != nil { + return 0, fmt.Errorf("read: %w", err) + } + + fset := token.NewFileSet() + parsed, err := parser.ParseFile(fset, clean, src, parser.ParseComments) + if err != nil { + return 0, fmt.Errorf("parse: %w", err) + } + var count int + for _, d := range parsed.Decls { + fd, ok := d.(*ast.FuncDecl) + if !ok || fd.Body == nil { // Skip declarations without bodies (interfaces, externs). + continue + } + var buf bytes.Buffer + for _, s := range fd.Body.List { + buf.WriteString(normalize(fmt.Sprintf("%#v", s))) + } + h := sha256.Sum256(buf.Bytes()) + key := fmt.Sprintf("%x", h) + loc := fmt.Sprintf("%s:%s", clean, fd.Name.Name) + funcMap[key] = append(funcMap[key], loc) + count++ + } + + return count, nil +} + +// normalize reduces insignificant differences in the AST statement dump so that +// logically identical bodies hash the same even if formatting varies. +func normalize(s string) string { + s = strings.ToLower(s) // case-insensitive + s = strings.Join(strings.Fields(s), " ") // collapse all whitespace runs + s = strings.ReplaceAll(s, "\n", " ") // remove line breaks entirely + + return s +} diff --git a/tools/docutil/normalize.go b/tools/docutil/normalize.go new file mode 100644 index 00000000..c05d8e5d --- /dev/null +++ b/tools/docutil/normalize.go @@ -0,0 +1,40 @@ +// Package docutil provides small helpers for documentation generators to share. +package docutil + +import ( + "bufio" + "strings" +) + +// NormalizeForCompare returns a version of the input with volatile header lines removed. +// Currently strips :created-date: and :revdate: lines so generators can perform +// stable comparisons and avoid rewriting files when only the date changes. +func NormalizeForCompare(s string) string { + var b strings.Builder + scanner := bufio.NewScanner(strings.NewReader(s)) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, ":created-date:") || strings.HasPrefix(line, ":revdate:") { + continue + } + b.WriteString(line) + b.WriteByte('\n') + } + // Trim trailing newline for consistency with previous implementation. + return strings.TrimSuffix(b.String(), "\n") +} + +// ExtractDateLine returns the value of a date-like header line matching the given prefix. +// Example: prefix ":created-date:" matches a line like ":created-date: March 23, 2025" and returns "March 23, 2025". +func ExtractDateLine(content, prefix string) string { + for _, line := range strings.Split(content, "\n") { + if strings.HasPrefix(line, prefix) { + parts := strings.SplitN(line, ": ", 2) + if len(parts) == 2 { + return parts[1] + } + } + } + + return "" +} diff --git a/tools/generate-command-docs/main.go b/tools/generate-command-docs/main.go new file mode 100644 index 00000000..e3bbedfa --- /dev/null +++ b/tools/generate-command-docs/main.go @@ -0,0 +1,383 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "os" + "path/filepath" + "sort" + "strings" + "text/template" + "time" + + "github.com/pingidentity/pingcli/cmd" + "github.com/pingidentity/pingcli/tools/docutil" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func main() { + outDir := flag.String("o", "./docs", "Output directory for AsciiDoc command pages") + date := flag.String("date", time.Now().Format("January 2, 2006"), "Created/revision date used in headers (e.g., March 23, 2025)") + resourcePrefix := flag.String("resource-prefix", "pingcli_command_reference_", "Prefix for :resourceid:") + version := flag.String("version", "dev", "Version string for root command init") + commit := flag.String("commit", "dev", "Commit SHA for root command init") + flag.Parse() + + root := cmd.NewRootCommand(*version, *commit) + root.DisableAutoGenTag = true + + // Use tighter directory permissions (group readable/executable only) to satisfy gosec G301. + // Not huge since these are just docs, but still better to be consistent. + if err := os.MkdirAll(*outDir, 0o750); err != nil { + fail("create out dir", err) + } + + // One file per command path with deterministic, content-based updates. + walkVisible(root, func(c *cobra.Command) { + base := strings.ReplaceAll(c.CommandPath(), " ", "_") + file := filepath.Join(*outDir, base+".adoc") + + // If file exists, extract existing created-date so it is preserved. + var existingCreated string + if oldRaw, err := readFileIfWithin(file, *outDir); err == nil { + existingCreated = docutil.ExtractDateLine(string(oldRaw), ":created-date:") + } + createdDate := *date + if existingCreated != "" { + createdDate = existingCreated + } + + content := renderSingle(c, createdDate, *date, *resourcePrefix) + + // Determine if underlying (non-date) content actually changed; if not, skip rewrite. + var prevBody string + if oldRaw, err := readFileIfWithin(file, *outDir); err == nil { + prevBody = docutil.NormalizeForCompare(string(oldRaw)) + } + newBody := docutil.NormalizeForCompare(content) + if prevBody == newBody && prevBody != "" { + // Skip updating revision date to avoid needless churn. + return + } + + // Restrict file permissions (no world access) for consistency with directory perms. + if err := os.WriteFile(file, []byte(content), 0o600); err != nil { + fail("write file "+file, err) + } + }) + + // Navigation file: only write if changed to keep diffs minimal. + navPath := filepath.Join(*outDir, "nav.adoc") + navContent := renderNav(root) + if oldNav, err := readFileIfWithin(navPath, *outDir); err == nil { + if string(oldNav) == navContent { + // Unchanged + return + } + } + if err := os.WriteFile(navPath, []byte(navContent), 0o600); err != nil { + fail("write nav file", err) + } +} + +func renderSingle(c *cobra.Command, createdDate, revDate, resourcePrefix string) string { + type singlePageData struct { + CommandPath string + CreatedDate string + RevDate string + ResourceID string + Short string + Synopsis string + Use string + ExampleBlock string + HasLocal bool + LocalOptions string + HasInherited bool + InheritedOptions string + ParentBlock string + SubcommandsBlock string + } + + // Precompute fields exactly matching previous output. + base := strings.ReplaceAll(c.CommandPath(), " ", "_") + short := strings.TrimSpace(firstLine(c.Short, c.Long)) + var synopsis string + if long := strings.TrimSpace(c.Long); long != "" { + synopsis = long + "\n\n" + } else if s := strings.TrimSpace(c.Short); s != "" { + synopsis = s + "\n\n" + } + use := strings.TrimSpace(c.UseLine()) + var exampleBlock string + if rawEx := c.Example; strings.TrimSpace(rawEx) != "" { + var eb strings.Builder + eb.WriteString("== Examples\n\n") + eb.WriteString("----\n") + eb.WriteString(rawEx) + if !strings.HasSuffix(rawEx, "\n") { + eb.WriteString("\n") + } + eb.WriteString("----\n\n") + exampleBlock = eb.String() + } + + local := c.NonInheritedFlags() + inherited := c.InheritedFlags() + hasLocal := local != nil && local.HasAvailableFlags() + hasInherited := inherited != nil && inherited.HasAvailableFlags() + var localBlock, inheritedBlock string + if hasLocal { + localBlock = formatFlagBlock(local, true, c) + } + if hasInherited { + inheritedBlock = formatFlagBlock(inherited, false, c) + } + + var parentBlock string + if p := c.Parent(); p != nil { + parentFile := strings.ReplaceAll(p.CommandPath(), " ", "_") + ".adoc" + var pb strings.Builder + pb.WriteString("== More information\n\n") + fmt.Fprintf(&pb, "* xref:%s[]\t - %s\n\n", parentFile, firstLine(p.Short, p.Long)) + parentBlock = pb.String() + } + + var subcommandsBlock string + subs := visibleSubcommands(c) + if len(subs) > 0 { + sort.Slice(subs, func(i, j int) bool { return subs[i].Name() < subs[j].Name() }) + var sb strings.Builder + sb.WriteString("== Subcommands\n\n") + for _, sc := range subs { + name := strings.ReplaceAll(sc.CommandPath(), " ", "_") + ".adoc" + fmt.Fprintf(&sb, "* xref:%s[] - %s\n", name, firstLine(sc.Short, sc.Long)) + } + sb.WriteString("\n") + subcommandsBlock = sb.String() + } + + data := singlePageData{ + CommandPath: c.CommandPath(), + CreatedDate: createdDate, + RevDate: revDate, + ResourceID: resourcePrefix + base, + Short: short, + Synopsis: synopsis, + Use: use, + ExampleBlock: exampleBlock, + HasLocal: hasLocal, + LocalOptions: localBlock, + HasInherited: hasInherited, + InheritedOptions: inheritedBlock, + ParentBlock: parentBlock, + SubcommandsBlock: subcommandsBlock, + } + + var buf bytes.Buffer + if err := singlePageTpl.Execute(&buf, data); err != nil { + // Fallback should never happen; keep previous behavior if it does. + return "" + } + + return buf.String() +} + +// formatFlagBlock renders a flag set into a code-fenced block similar to manual pages. +func formatFlagBlock(fs *pflag.FlagSet, includeHelp bool, c *cobra.Command) string { + var flags []*pflag.Flag + fs.VisitAll(func(f *pflag.Flag) { flags = append(flags, f) }) + sort.Slice(flags, func(i, j int) bool { + si, sj := flags[i].Shorthand, flags[j].Shorthand + if si == sj { + return flags[i].Name < flags[j].Name + } + if si == "" { + return false + } + if sj == "" { + return true + } + + return si < sj + }) + type line struct { + Spec string + Pad int + Desc string + } + lines := make([]line, 0, len(flags)) + for _, f := range flags { + var spec string + if f.Shorthand != "" { + spec = fmt.Sprintf("-%s, --%s", f.Shorthand, f.Name) + } else { + spec = fmt.Sprintf(" --%s", f.Name) + } + typeName := f.Value.Type() + if typeName != "bool" { + spec += " " + typeName + } + desc := f.Usage + if typeName == "bool" { + // Add only if usage text does not already contain a default and DefValue is meaningful. + if !strings.Contains(desc, "(default") && f.DefValue != "" { + desc = fmt.Sprintf("%s (default %s)", desc, f.DefValue) + } + } else if f.DefValue != "" && f.DefValue != "" && f.DefValue != "0" && !strings.Contains(desc, "(default") { + desc = fmt.Sprintf("%s (default %s)", desc, f.DefValue) + } + + // Collapse internal newlines but otherwise keep original spacing; no manual wrapping. + desc = strings.ReplaceAll(desc, "\n", " ") + + lines = append(lines, line{Spec: spec, Desc: desc}) + } + if includeHelp { + found := false + for _, l := range lines { + if strings.Contains(l.Spec, "--help") { + found = true + + break + } + } + if !found { + helpLine := line{Spec: "-h, --help", Desc: fmt.Sprintf("help for %s", c.Name())} + if len(lines) == 0 { + lines = append(lines, helpLine) + } else { + lines = append(lines[:1], append([]line{helpLine}, lines[1:]...)...) + } + } + } + maxSpec := 0 + for _, l := range lines { + if len(l.Spec) > maxSpec { + maxSpec = len(l.Spec) + } + } + for i := range lines { + pad := maxSpec - len(lines[i].Spec) + if pad < 0 { + pad = 0 + } + lines[i].Pad = pad + } + var buf bytes.Buffer + if err := flagBlockTpl.Execute(&buf, struct{ Lines []line }{Lines: lines}); err != nil { + return "" + } + + return buf.String() +} + +// firstLine returns the first non-empty line from short or long description. +func firstLine(short, long string) string { + if strings.TrimSpace(short) != "" { + return strings.SplitN(strings.TrimSpace(short), "\n", 2)[0] + } + if strings.TrimSpace(long) != "" { + return strings.SplitN(strings.TrimSpace(long), "\n", 2)[0] + } + + return "" +} + +func visibleSubcommands(c *cobra.Command) []*cobra.Command { + cmds := c.Commands() + subs := make([]*cobra.Command, 0, len(cmds)) + for _, sc := range cmds { + if sc.Hidden { + continue + } + subs = append(subs, sc) + } + + return subs +} + +func walkVisible(c *cobra.Command, fn func(*cobra.Command)) { + fn(c) + children := visibleSubcommands(c) + sort.Slice(children, func(i, j int) bool { return children[i].Name() < children[j].Name() }) + for _, sc := range children { + walkVisible(sc, fn) + } +} + +func depthOf(c *cobra.Command) int { return len(strings.Split(c.CommandPath(), " ")) } + +func fail(doing string, err error) { + fmt.Fprintf(os.Stderr, "error while %s: %v\n", doing, err) + os.Exit(1) +} + +// renderNav builds nav.adoc content with hierarchical bullet list. +// Format mirrors manually created original nav.adoc but with synthetic top-level group. +func renderNav(root *cobra.Command) string { + var b strings.Builder + b.WriteString("* Command Reference\n") + + // Add root command first + rootFile := strings.ReplaceAll(root.CommandPath(), " ", "_") + ".adoc" + fmt.Fprintf(&b, "** xref:command_reference:%s[]\n", rootFile) + + // Add all other commands + walkVisible(root, func(c *cobra.Command) { + if c == root { + return + } + stars := strings.Repeat("*", depthOf(c)) + file := strings.ReplaceAll(c.CommandPath(), " ", "_") + ".adoc" + fmt.Fprintf(&b, "%s xref:command_reference:%s[]\n", stars, file) + }) + b.WriteString("\n") + + return b.String() +} + +// readFileIfWithin validates that path is within base before reading to satisfy gosec G304. +func readFileIfWithin(path, base string) ([]byte, error) { + cleanBase := filepath.Clean(base) + cleanPath := filepath.Clean(path) + if !strings.HasPrefix(cleanPath+string(os.PathSeparator), cleanBase+string(os.PathSeparator)) { + return nil, fmt.Errorf("refusing to read path outside base directory: %s", path) + } + data, err := os.ReadFile(cleanPath) // #nosec G304 path validated above + if err != nil { + return nil, err + } + + return data, nil +} + +// Templates and helpers +var singlePageTpl = template.Must(template.New("single").Parse(`= {{.CommandPath}} +:created-date: {{.CreatedDate}} +:revdate: {{.RevDate}} +:resourceid: {{.ResourceID}} + +{{if .Short}}{{.Short}} + +{{end}}== Synopsis + +{{.Synopsis}}---- +{{.Use}} +---- + +{{if .ExampleBlock}}{{.ExampleBlock}}{{end}}{{if .HasLocal}}== Options + +{{.LocalOptions}} +{{end}}{{if .HasInherited}}== Options inherited from parent commands + +{{.InheritedOptions}} +{{end}}{{if .ParentBlock}}{{.ParentBlock}}{{end}}{{if .SubcommandsBlock}}{{.SubcommandsBlock}}{{end}}`)) + +func repeat(n int) string { return strings.Repeat(" ", n) } + +var flagBlockTpl = template.Must(template.New("flag").Funcs(template.FuncMap{"repeat": repeat}).Parse(`---- +{{range .Lines}} {{.Spec}}{{repeat .Pad}} {{.Desc}} +{{end}}---- +`)) diff --git a/tools/generate-command-docs/main_test.go b/tools/generate-command-docs/main_test.go new file mode 100644 index 00000000..cc52b852 --- /dev/null +++ b/tools/generate-command-docs/main_test.go @@ -0,0 +1,64 @@ +package main + +import ( + "flag" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/pingidentity/pingcli/tools/docutil" +) + +var update = flag.Bool("update", false, "update golden files for command docs") + +// TestCommandDocGeneration generates documentation for the real root command and compares +// a subset of produced files (root command + nav) against golden fixtures. +func TestCommandDocGeneration(t *testing.T) { + flag.Parse() + + tmp := t.TempDir() + // Run the generator with deterministic date so golden files are stable. + date := "January 2, 2006" // Intentional fixed sample date + os.Args = []string{"docgen", "-o", tmp, "-date", date} + main() + + goldenDir := filepath.Join("testdata", "golden") + if err := os.MkdirAll(goldenDir, 0o750); err != nil { + t.Fatalf("mkdir golden: %v", err) + } + + files := []string{"pingcli.adoc", "nav.adoc"} + + for _, f := range files { + gotPath := filepath.Join(tmp, f) + // Validate generated file path stays within temporary directory (mitigates G304) + cleanGot := filepath.Clean(gotPath) + if !strings.HasPrefix(cleanGot+string(os.PathSeparator), filepath.Clean(tmp)+string(os.PathSeparator)) { + t.Fatalf("generated file path %s is outside of temp directory", gotPath) + } + gotBytes, err := os.ReadFile(cleanGot) // #nosec G304 path validated above + if err != nil { + t.Fatalf("read generated %s: %v", f, err) + } + got := docutil.NormalizeForCompare(string(gotBytes)) + + goldenPath := filepath.Join(goldenDir, f) + if *update { + if err := os.WriteFile(goldenPath, []byte(got), 0o600); err != nil { + t.Fatalf("write golden %s: %v", f, err) + } + t.Logf("updated golden: %s", f) + + continue + } + wantBytes, err := os.ReadFile(goldenPath) // #nosec G304 path validated above + if err != nil { + t.Fatalf("read golden %s: %v (run with -update to create)", f, err) + } + want := docutil.NormalizeForCompare(string(wantBytes)) + if got != want { + t.Errorf("mismatch for %s\n--- got ---\n%s\n--- want ---\n%s", f, got, want) + } + } +} diff --git a/tools/generate-command-docs/testdata/golden/README.txt b/tools/generate-command-docs/testdata/golden/README.txt new file mode 100644 index 00000000..e2fa0931 --- /dev/null +++ b/tools/generate-command-docs/testdata/golden/README.txt @@ -0,0 +1,6 @@ +Golden files for command doc generation. Update with: + go test -v ./tools/generate-command-docs -update + +Files captured: + pingcli.adoc - root command page (dates stripped on comparison) + nav.adoc - navigation structure diff --git a/tools/generate-command-docs/testdata/golden/nav.adoc b/tools/generate-command-docs/testdata/golden/nav.adoc new file mode 100644 index 00000000..4d0e1cc1 --- /dev/null +++ b/tools/generate-command-docs/testdata/golden/nav.adoc @@ -0,0 +1,23 @@ +* Command Reference +** xref:command_reference:pingcli.adoc[] +** xref:command_reference:pingcli_completion.adoc[] +** xref:command_reference:pingcli_config.adoc[] +*** xref:command_reference:pingcli_config_add-profile.adoc[] +*** xref:command_reference:pingcli_config_delete-profile.adoc[] +*** xref:command_reference:pingcli_config_get.adoc[] +*** xref:command_reference:pingcli_config_list-keys.adoc[] +*** xref:command_reference:pingcli_config_list-profiles.adoc[] +*** xref:command_reference:pingcli_config_set.adoc[] +*** xref:command_reference:pingcli_config_set-active-profile.adoc[] +*** xref:command_reference:pingcli_config_unset.adoc[] +*** xref:command_reference:pingcli_config_view-profile.adoc[] +** xref:command_reference:pingcli_feedback.adoc[] +** xref:command_reference:pingcli_license.adoc[] +** xref:command_reference:pingcli_platform.adoc[] +*** xref:command_reference:pingcli_platform_export.adoc[] +** xref:command_reference:pingcli_plugin.adoc[] +*** xref:command_reference:pingcli_plugin_add.adoc[] +*** xref:command_reference:pingcli_plugin_list.adoc[] +*** xref:command_reference:pingcli_plugin_remove.adoc[] +** xref:command_reference:pingcli_request.adoc[] + diff --git a/tools/generate-command-docs/testdata/golden/pingcli.adoc b/tools/generate-command-docs/testdata/golden/pingcli.adoc new file mode 100644 index 00000000..a08126f2 --- /dev/null +++ b/tools/generate-command-docs/testdata/golden/pingcli.adoc @@ -0,0 +1,34 @@ += pingcli +:resourceid: pingcli_command_reference_pingcli + +A CLI tool for managing the configuration of Ping Identity products. + +== Synopsis + +A CLI tool for managing the configuration of Ping Identity products. + +---- +pingcli +---- + +== Options + +---- + -C, --config string The relative or full path to a custom Ping CLI configuration file. (default $HOME/.pingcli/config.yaml) + -h, --help help for pingcli + -D, --detailed-exitcode Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. + -O, --output-format string Specify the console output format. (default text) Options are: json, text. + -P, --profile string The name of a configuration profile to use. + --no-color Disable text output in color. (default false) +---- + +== Subcommands + +* xref:pingcli_completion.adoc[] - Prints shell completion scripts +* xref:pingcli_config.adoc[] - Manage the CLI configuration. +* xref:pingcli_feedback.adoc[] - Help us improve the CLI. Report issues or send us feedback on using the CLI tool. +* xref:pingcli_license.adoc[] - Request a new evaluation license. +* xref:pingcli_platform.adoc[] - Administer and manage the Ping integrated platform. +* xref:pingcli_plugin.adoc[] - Manage Ping CLI plugins. +* xref:pingcli_request.adoc[] - Send a custom REST API request to a Ping platform service. + diff --git a/tools/generate-options-docs/docgen/docgen.go b/tools/generate-options-docs/docgen/docgen.go new file mode 100644 index 00000000..29d4bf86 --- /dev/null +++ b/tools/generate-options-docs/docgen/docgen.go @@ -0,0 +1,234 @@ +package docgen + +import ( + "fmt" + "path/filepath" + "slices" + "strings" + "time" + + "github.com/pingidentity/pingcli/internal/configuration" + "github.com/pingidentity/pingcli/internal/configuration/options" +) + +// GenerateMarkdown renders the options documentation markdown table sections. +func GenerateMarkdown() string { + configuration.InitAllOptions() + propertyCategoryInformation := make(map[string][]string) + for _, option := range options.Options() { + if option.KoanfKey == "" || option.Flag == nil { + continue + } + var flagInfo string + if option.Flag.Shorthand != "" { + flagInfo = fmt.Sprintf("--%s / -%s", option.CobraParamName, option.Flag.Shorthand) + } else { + flagInfo = fmt.Sprintf("--%s", option.CobraParamName) + } + usageString := strings.ReplaceAll(option.Flag.Usage, "\n", "

") + category := "general" + if strings.Contains(option.KoanfKey, ".") { + category = strings.Split(option.KoanfKey, ".")[0] + } + // New column order: Config Key | Equivalent Parameter | Environment Variable | Type | Purpose + propertyCategoryInformation[category] = append(propertyCategoryInformation[category], fmt.Sprintf("| %s | %s | %s | %d | %s |", option.KoanfKey, flagInfo, formatEnvVar(option.EnvVar), option.Type, usageString)) + } + var outputBuilder strings.Builder + cats := make([]string, 0, len(propertyCategoryInformation)) + for k := range propertyCategoryInformation { + cats = append(cats, k) + } + slices.Sort(cats) + for _, category := range cats { + properties := propertyCategoryInformation[category] + slices.Sort(properties) + outputBuilder.WriteString(fmt.Sprintf("#### %s Properties\n\n", category)) + outputBuilder.WriteString("| Config File Property | Equivalent Parameter | Environment Variable | Type | Purpose |\n") + outputBuilder.WriteString("|---|---|---|---|---|\n") + for _, property := range properties { + outputBuilder.WriteString(property + "\n") + } + outputBuilder.WriteString("\n") + } + + return outputBuilder.String() +} + +// GenerateAsciiDoc generates a configuration reference in AsciiDoc format. +func GenerateAsciiDoc() string { // backward-compatible wrapper using legacy date behavior + created := "March 23, 2025" + revdate := time.Now().Format("January 2, 2006") + + return GenerateAsciiDocWithDates(created, revdate) +} + +// GenerateAsciiDocWithDates renders AsciiDoc with explicit created and revision dates. +func GenerateAsciiDocWithDates(created, revdate string) string { + configuration.InitAllOptions() + // Dynamically detect categories from the first segment of KoanfKey (before '.') + // Use "general" for keys without a dot. + catMap := map[string][]options.Option{} + for _, opt := range options.Options() { + if opt.KoanfKey == "" { + continue + } + category := "general" + if i := strings.Index(opt.KoanfKey, "."); i > 0 { + category = opt.KoanfKey[:i] + } else if strings.Contains(opt.KoanfKey, ".") { + // Defensive: if dot at position 0 for some reason, fallback to general + category = "general" + } else if strings.Contains(opt.KoanfKey, ".") { + category = strings.Split(opt.KoanfKey, ".")[0] + } + catMap[category] = append(catMap[category], opt) + } + for k := range catMap { + slices.SortFunc(catMap[k], func(a, b options.Option) int { return strings.Compare(a.KoanfKey, b.KoanfKey) }) + } + var b strings.Builder + b.WriteString("= Configuration Settings Reference\n") + b.WriteString(fmt.Sprintf(":created-date: %s\n", created)) + b.WriteString(fmt.Sprintf(":revdate: %s\n", revdate)) + b.WriteString(":resourceid: pingcli_configuration_settings_reference\n\n") + b.WriteString("The following configuration settings can be applied when using Ping CLI.\n\n") + b.WriteString("The following configuration settings can be applied by using the xref:command_reference:pingcli_config_set.adoc[`config set` command] to persist the configuration value for a given **Configuration Key** in the Ping CLI configuration file.\n\n") + b.WriteString("The configuration file is created at `.pingcli/config.yaml` in the user's home directory.\n\n") + // Determine output order: prefer known categories in a fixed order when present, + // then append any other categories sorted alphabetically. This keeps current + // docs stable while allowing new categories to appear without code changes. + preferred := []string{"general", "service", "export", "license", "request"} + seen := map[string]bool{} + var keys []string + for _, k := range preferred { + if _, ok := catMap[k]; ok { + keys = append(keys, k) + seen[k] = true + } + } + // Add any additional categories not in preferred, sorted. + var extras []string + for k := range catMap { + if !seen[k] { + extras = append(extras, k) + } + } + slices.Sort(extras) + keys = append(keys, extras...) + + for _, k := range keys { + opts := catMap[k] + if len(opts) == 0 { + continue + } + b.WriteString("== " + sectionTitle(k) + "\n\n") + // Column order updated: Configuration Key | Equivalent Parameter | Environment Variable | Data Type | Purpose + b.WriteString("[cols=\"2,2,2,1,3\"]\n|===\n") + b.WriteString("|Configuration Key |Equivalent Parameter |Environment Variable |Data Type |Purpose\n\n") + for _, opt := range opts { + key := normalizeAsciiDocKey(opt.KoanfKey) + dataType := asciiDocDataType(opt) + eqParam := asciiDocEquivalentParameter(opt) + envVar := opt.EnvVar + purpose := sanitizeUsage(opt) + b.WriteString(fmt.Sprintf("| `%s` | %s | %s | %s | %s\n", key, eqParam, formatEnvVar(envVar), dataType, purpose)) + } + b.WriteString("|===\n\n") + } + + return b.String() +} + +// sectionTitle returns a friendly section title for a category key, with special +// wording for well-known categories and a sensible default otherwise. +func sectionTitle(key string) string { + switch key { + case "general": + return "General Properties" + case "service": + return "Ping Identity Platform Service Properties" + case "export": + return "Platform Export Properties" + case "license": + return "License Properties" + case "request": + return "Custom Request Properties" + default: + if key == "" { + return "Properties" + } + // Capitalize first rune; avoid pulling in extra deps. + r := []rune(key) + r[0] = []rune(strings.ToUpper(string(r[0])))[0] + + return string(r) + " properties" + } +} + +// ShouldOutputAsciiDoc determines if AsciiDoc format should be used based on file extension or explicit choice. +func ShouldOutputAsciiDoc(outPath string, explicit bool) bool { + if explicit { + return true + } + ext := strings.ToLower(filepath.Ext(outPath)) + + return ext == ".adoc" || ext == ".asciidoc" +} + +// Helper functions for AsciiDoc generation +func asciiDocEquivalentParameter(opt options.Option) string { + if opt.Flag == nil { + return "" + } + if opt.Flag.Shorthand != "" { + return fmt.Sprintf("`--%s` / `-%s`", opt.CobraParamName, opt.Flag.Shorthand) + } + + return fmt.Sprintf("`--%s`", opt.CobraParamName) +} + +func asciiDocDataType(opt options.Option) string { + switch opt.Type { + case options.BOOL: + return "Boolean" + case options.STRING, options.LICENSE_VERSION: + return "String" + case options.STRING_SLICE, options.EXPORT_SERVICES, options.HEADER: + return "String Array" + case options.UUID: + return "String (UUID Format)" + case options.EXPORT_FORMAT, options.OUTPUT_FORMAT, options.PINGFEDERATE_AUTH_TYPE, options.PINGONE_AUTH_TYPE, options.PINGONE_REGION_CODE, options.REQUEST_SERVICE, options.EXPORT_SERVICE_GROUP, options.LICENSE_PRODUCT: + return "String (Enum)" + case options.INT: + return "Integer" + default: + // Use an explicit fallback so unmapped types surface during review. + return "N/A" + } +} + +func sanitizeUsage(opt options.Option) string { + if opt.Flag == nil { + return "" + } + usage := opt.Flag.Usage + usage = strings.ReplaceAll(usage, "

", " ") + usage = strings.ReplaceAll(usage, "\n", " ") + usage = strings.TrimSpace(usage) + + return usage +} + +func normalizeAsciiDocKey(key string) string { + key = strings.ReplaceAll(key, "pingFederate", "pingfederate") + key = strings.ReplaceAll(key, "pingOne", "pingone") + key = strings.ReplaceAll(key, "PEMFiles", "PemFiles") + + return key +} + +// formatEnvVar returns the environment variable name or an empty string if not set. +// This indirection keeps table generation simpler and allows future formatting changes. +func formatEnvVar(s string) string { + return strings.TrimSpace(s) +} diff --git a/tools/generate-options-docs/docgen/docgen_test.go b/tools/generate-options-docs/docgen/docgen_test.go new file mode 100644 index 00000000..39a3180b --- /dev/null +++ b/tools/generate-options-docs/docgen/docgen_test.go @@ -0,0 +1,63 @@ +package docgen_test + +import ( + "flag" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/pingidentity/pingcli/tools/docutil" + docgen "github.com/pingidentity/pingcli/tools/generate-options-docs/docgen" +) + +var update = flag.Bool("update", false, "update golden files for options docs") + +// TestOptionsDocGeneration validates both markdown and AsciiDoc outputs against goldens. +func TestOptionsDocGeneration(t *testing.T) { + flag.Parse() + + md := docgen.GenerateMarkdown() + adoc := docgen.GenerateAsciiDoc() + + goldenDir := filepath.Join("testdata", "golden") + if err := os.MkdirAll(goldenDir, 0o750); err != nil { // tighter perms + t.Fatalf("mkdir golden: %v", err) + } + + // Normalize dynamic date in AsciiDoc output before storing / comparing. + adocNorm := docutil.NormalizeForCompare(adoc) + + cases := []struct { + name string + content string + }{ + {"options.md", md}, + {"options.adoc", adocNorm}, + } + + for _, tc := range cases { + goldenPath := filepath.Join(goldenDir, tc.name) + // Validate golden file path remains within goldenDir to mitigate G304 + cleanGolden := filepath.Clean(goldenPath) + if !strings.HasPrefix(cleanGolden+string(os.PathSeparator), filepath.Clean(goldenDir)+string(os.PathSeparator)) { + t.Fatalf("invalid golden file path: %s", goldenPath) + } + if *update { + if err := os.WriteFile(cleanGolden, []byte(tc.content), 0o600); err != nil { + t.Fatalf("write golden %s: %v", tc.name, err) + } + t.Logf("updated golden: %s", tc.name) + + continue + } + wantBytes, err := os.ReadFile(cleanGolden) // #nosec G304 path validated + if err != nil { + t.Fatalf("read golden %s: %v (run with -update to create)", tc.name, err) + } + want := string(wantBytes) + if tc.content != want { + t.Errorf("mismatch for %s\n--- got ---\n%s\n--- want ---\n%s", tc.name, tc.content, want) + } + } +} diff --git a/tools/generate-options-docs/docgen/testdata/golden/README.txt b/tools/generate-options-docs/docgen/testdata/golden/README.txt new file mode 100644 index 00000000..265caefc --- /dev/null +++ b/tools/generate-options-docs/docgen/testdata/golden/README.txt @@ -0,0 +1,6 @@ +Golden files for options doc generation. Update with: + go test -v ./tools/generate-options-docs/docgen -update + +Files captured: + options.md - markdown table output + options.adoc - asciidoc configuration reference (dates stripped on comparison) diff --git a/tools/generate-options-docs/docgen/testdata/golden/options.adoc b/tools/generate-options-docs/docgen/testdata/golden/options.adoc new file mode 100644 index 00000000..fbceb3dd --- /dev/null +++ b/tools/generate-options-docs/docgen/testdata/golden/options.adoc @@ -0,0 +1,84 @@ += Configuration Settings Reference +:resourceid: pingcli_configuration_settings_reference + +The following configuration settings can be applied when using Ping CLI. + +The following configuration settings can be applied by using the xref:command_reference:pingcli_config_set.adoc[`config set` command] to persist the configuration value for a given **Configuration Key** in the Ping CLI configuration file. + +The configuration file is created at `.pingcli/config.yaml` in the user's home directory. + +== General Properties + +[cols="2,2,2,1,3"] +|=== +|Configuration Key |Equivalent Parameter |Environment Variable |Data Type |Purpose + +| `activeProfile` | | | String | +| `description` | | | String | +| `detailedExitCode` | `--detailed-exitcode` / `-D` | PINGCLI_DETAILED_EXITCODE | Boolean | Enable detailed exit code output. (default false) 0 - pingcli command succeeded with no errors or warnings. 1 - pingcli command failed with errors. 2 - pingcli command succeeded with warnings. +| `noColor` | `--no-color` | PINGCLI_NO_COLOR | Boolean | Disable text output in color. (default false) +| `outputFormat` | `--output-format` / `-O` | PINGCLI_OUTPUT_FORMAT | String (Enum) | Specify the console output format. (default text) Options are: json, text. +| `plugins` | | | String Array | +|=== + +== Ping Identity Platform Service Properties + +[cols="2,2,2,1,3"] +|=== +|Configuration Key |Equivalent Parameter |Environment Variable |Data Type |Purpose + +| `service.pingfederate.adminAPIPath` | `--pingfederate-admin-api-path` | PINGCLI_PINGFEDERATE_ADMIN_API_PATH | String | The PingFederate API URL path used to communicate with PingFederate's admin API. (default /pf-admin-api/v1) +| `service.pingfederate.authentication.accessTokenAuth.accessToken` | `--pingfederate-access-token` | PINGCLI_PINGFEDERATE_ACCESS_TOKEN | String | The PingFederate access token used to authenticate to the PingFederate admin API when using a custom OAuth 2.0 token method. +| `service.pingfederate.authentication.basicAuth.password` | `--pingfederate-password` | PINGCLI_PINGFEDERATE_PASSWORD | String | The PingFederate password used to authenticate to the PingFederate admin API when using basic authentication. +| `service.pingfederate.authentication.basicAuth.username` | `--pingfederate-username` | PINGCLI_PINGFEDERATE_USERNAME | String | The PingFederate username used to authenticate to the PingFederate admin API when using basic authentication. Example: 'administrator' +| `service.pingfederate.authentication.clientCredentialsAuth.clientID` | `--pingfederate-client-id` | PINGCLI_PINGFEDERATE_CLIENT_ID | String | The PingFederate OAuth client ID used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. +| `service.pingfederate.authentication.clientCredentialsAuth.clientSecret` | `--pingfederate-client-secret` | PINGCLI_PINGFEDERATE_CLIENT_SECRET | String | The PingFederate OAuth client secret used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. +| `service.pingfederate.authentication.clientCredentialsAuth.scopes` | `--pingfederate-scopes` | PINGCLI_PINGFEDERATE_SCOPES | String Array | The PingFederate OAuth scopes used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. (default []) Accepts a comma-separated string to delimit multiple scopes. Example: 'openid,profile' +| `service.pingfederate.authentication.clientCredentialsAuth.tokenURL` | `--pingfederate-token-url` | PINGCLI_PINGFEDERATE_TOKEN_URL | String | The PingFederate OAuth token URL used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. +| `service.pingfederate.authentication.type` | `--pingfederate-authentication-type` | PINGCLI_PINGFEDERATE_AUTHENTICATION_TYPE | String (Enum) | The authentication type to use when connecting to the PingFederate admin API. Options are: accessTokenAuth, basicAuth, clientCredentialsAuth. Example: 'basicAuth' +| `service.pingfederate.caCertificatePemFiles` | `--pingfederate-ca-certificate-pem-files` | PINGCLI_PINGFEDERATE_CA_CERTIFICATE_PEM_FILES | String Array | Relative or full paths to PEM-encoded certificate files to be trusted as root CAs when connecting to the PingFederate server over HTTPS. (default []) Accepts a comma-separated string to delimit multiple PEM files. +| `service.pingfederate.httpsHost` | `--pingfederate-https-host` | PINGCLI_PINGFEDERATE_HTTPS_HOST | String | The PingFederate HTTPS host used to communicate with PingFederate's admin API. Example: 'https://pingfederate-admin.bxretail.org' +| `service.pingfederate.insecureTrustAllTLS` | `--pingfederate-insecure-trust-all-tls` | PINGCLI_PINGFEDERATE_INSECURE_TRUST_ALL_TLS | Boolean | Trust any certificate when connecting to the PingFederate server admin API. (default false) This is insecure and shouldn't be enabled outside of testing. +| `service.pingfederate.xBypassExternalValidationHeader` | `--pingfederate-x-bypass-external-validation-header` | PINGCLI_PINGFEDERATE_X_BYPASS_EXTERNAL_VALIDATION_HEADER | Boolean | Bypass connection tests when configuring PingFederate (the X-BypassExternalValidation header when using PingFederate's admin API). (default false) +| `service.pingone.authentication.type` | `--pingone-authentication-type` | PINGCLI_PINGONE_AUTHENTICATION_TYPE | String (Enum) | The authentication type to use to authenticate to the PingOne management API. (default worker) Options are: worker. +| `service.pingone.authentication.worker.clientID` | `--pingone-worker-client-id` | PINGCLI_PINGONE_WORKER_CLIENT_ID | String (UUID Format) | The worker client ID used to authenticate to the PingOne management API. +| `service.pingone.authentication.worker.clientSecret` | `--pingone-worker-client-secret` | PINGCLI_PINGONE_WORKER_CLIENT_SECRET | String | The worker client secret used to authenticate to the PingOne management API. +| `service.pingone.authentication.worker.environmentID` | `--pingone-worker-environment-id` | PINGCLI_PINGONE_WORKER_ENVIRONMENT_ID | String (UUID Format) | The ID of the PingOne environment that contains the worker client used to authenticate to the PingOne management API. +| `service.pingone.regionCode` | `--pingone-region-code` | PINGCLI_PINGONE_REGION_CODE | String (Enum) | The region code of the PingOne tenant. Options are: AP, AU, CA, EU, NA. Example: 'NA' +|=== + +== Platform Export Properties + +[cols="2,2,2,1,3"] +|=== +|Configuration Key |Equivalent Parameter |Environment Variable |Data Type |Purpose + +| `export.format` | `--format` / `-f` | PINGCLI_EXPORT_FORMAT | String (Enum) | Specifies the export format. (default HCL) Options are: HCL. +| `export.outputDirectory` | `--output-directory` / `-d` | PINGCLI_EXPORT_OUTPUT_DIRECTORY | String | Specifies the output directory for export. Can be an absolute filepath or a relative filepath of the present working directory. Example: '/Users/example/pingcli-export' Example: 'pingcli-export' +| `export.overwrite` | `--overwrite` / `-o` | PINGCLI_EXPORT_OVERWRITE | Boolean | Overwrites the existing generated exports in output directory. (default false) +| `export.pingone.environmentID` | `--pingone-export-environment-id` | PINGCLI_PINGONE_EXPORT_ENVIRONMENT_ID | String (UUID Format) | The ID of the PingOne environment to export. Must be a valid PingOne UUID. +| `export.serviceGroup` | `--service-group` / `-g` | PINGCLI_EXPORT_SERVICE_GROUP | String (Enum) | Specifies the service group to export. Options are: pingone. Example: 'pingone' +| `export.services` | `--services` / `-s` | PINGCLI_EXPORT_SERVICES | String Array | Specifies the service(s) to export. Accepts a comma-separated string to delimit multiple services. Options are: pingfederate, pingone-authorize, pingone-mfa, pingone-platform, pingone-protect, pingone-sso. Example: 'pingone-sso,pingone-mfa,pingfederate' +|=== + +== License Properties + +[cols="2,2,2,1,3"] +|=== +|Configuration Key |Equivalent Parameter |Environment Variable |Data Type |Purpose + +| `license.devopsKey` | `--devops-key` / `-k` | PINGCLI_LICENSE_DEVOPS_KEY | String | The DevOps key for the license request. See https://developer.pingidentity.com/devops/how-to/devopsRegistration.html on how to register a DevOps user. You can save the DevOps user and key in your profile using the 'pingcli config' commands. +| `license.devopsUser` | `--devops-user` / `-u` | PINGCLI_LICENSE_DEVOPS_USER | String | The DevOps user for the license request. See https://developer.pingidentity.com/devops/how-to/devopsRegistration.html on how to register a DevOps user. You can save the DevOps user and key in your profile using the 'pingcli config' commands. +|=== + +== Custom Request Properties + +[cols="2,2,2,1,3"] +|=== +|Configuration Key |Equivalent Parameter |Environment Variable |Data Type |Purpose + +| `request.accessToken` | | | String | +| `request.accessTokenExpiry` | | | Integer | +| `request.fail` | `--fail` / `-f` | | Boolean | Return non-zero exit code when HTTP custom request returns a failure status code. +| `request.service` | `--service` / `-s` | PINGCLI_REQUEST_SERVICE | String (Enum) | The Ping service (configured in the active profile) to send the custom request to. Options are: pingone. Example: 'pingone' +|=== diff --git a/tools/generate-options-docs/docgen/testdata/golden/options.md b/tools/generate-options-docs/docgen/testdata/golden/options.md new file mode 100644 index 00000000..95ce4867 --- /dev/null +++ b/tools/generate-options-docs/docgen/testdata/golden/options.md @@ -0,0 +1,56 @@ +#### export Properties + +| Config File Property | Equivalent Parameter | Environment Variable | Type | Purpose | +|---|---|---|---|---| +| export.format | --format / -f | PINGCLI_EXPORT_FORMAT | 1 | Specifies the export format. (default HCL)

Options are: HCL. | +| export.outputDirectory | --output-directory / -d | PINGCLI_EXPORT_OUTPUT_DIRECTORY | 14 | Specifies the output directory for export. Can be an absolute filepath or a relative filepath of the present working directory.

Example: '/Users/example/pingcli-export'

Example: 'pingcli-export' | +| export.overwrite | --overwrite / -o | PINGCLI_EXPORT_OVERWRITE | 0 | Overwrites the existing generated exports in output directory. (default false) | +| export.pingOne.environmentID | --pingone-export-environment-id | PINGCLI_PINGONE_EXPORT_ENVIRONMENT_ID | 16 | The ID of the PingOne environment to export. Must be a valid PingOne UUID. | +| export.serviceGroup | --service-group / -g | PINGCLI_EXPORT_SERVICE_GROUP | 2 | Specifies the service group to export.

Options are: pingone.

Example: 'pingone' | +| export.services | --services / -s | PINGCLI_EXPORT_SERVICES | 3 | Specifies the service(s) to export. Accepts a comma-separated string to delimit multiple services.

Options are: pingfederate, pingone-authorize, pingone-mfa, pingone-platform, pingone-protect, pingone-sso.

Example: 'pingone-sso,pingone-mfa,pingfederate' | + +#### general Properties + +| Config File Property | Equivalent Parameter | Environment Variable | Type | Purpose | +|---|---|---|---|---| +| detailedExitCode | --detailed-exitcode / -D | PINGCLI_DETAILED_EXITCODE | 0 | Enable detailed exit code output. (default false)

0 - pingcli command succeeded with no errors or warnings.

1 - pingcli command failed with errors.

2 - pingcli command succeeded with warnings. | +| noColor | --no-color | PINGCLI_NO_COLOR | 0 | Disable text output in color. (default false) | +| outputFormat | --output-format / -O | PINGCLI_OUTPUT_FORMAT | 8 | Specify the console output format. (default text)

Options are: json, text. | + +#### license Properties + +| Config File Property | Equivalent Parameter | Environment Variable | Type | Purpose | +|---|---|---|---|---| +| license.devopsKey | --devops-key / -k | PINGCLI_LICENSE_DEVOPS_KEY | 14 | The DevOps key for the license request.

See https://developer.pingidentity.com/devops/how-to/devopsRegistration.html on how to register a DevOps user.

You can save the DevOps user and key in your profile using the 'pingcli config' commands. | +| license.devopsUser | --devops-user / -u | PINGCLI_LICENSE_DEVOPS_USER | 14 | The DevOps user for the license request.

See https://developer.pingidentity.com/devops/how-to/devopsRegistration.html on how to register a DevOps user.

You can save the DevOps user and key in your profile using the 'pingcli config' commands. | + +#### request Properties + +| Config File Property | Equivalent Parameter | Environment Variable | Type | Purpose | +|---|---|---|---|---| +| request.fail | --fail / -f | | 0 | Return non-zero exit code when HTTP custom request returns a failure status code. | +| request.service | --service / -s | PINGCLI_REQUEST_SERVICE | 13 | The Ping service (configured in the active profile) to send the custom request to.

Options are: pingone.

Example: 'pingone' | + +#### service Properties + +| Config File Property | Equivalent Parameter | Environment Variable | Type | Purpose | +|---|---|---|---|---| +| service.pingFederate.adminAPIPath | --pingfederate-admin-api-path | PINGCLI_PINGFEDERATE_ADMIN_API_PATH | 14 | The PingFederate API URL path used to communicate with PingFederate's admin API. (default /pf-admin-api/v1) | +| service.pingFederate.authentication.accessTokenAuth.accessToken | --pingfederate-access-token | PINGCLI_PINGFEDERATE_ACCESS_TOKEN | 14 | The PingFederate access token used to authenticate to the PingFederate admin API when using a custom OAuth 2.0 token method. | +| service.pingFederate.authentication.basicAuth.password | --pingfederate-password | PINGCLI_PINGFEDERATE_PASSWORD | 14 | The PingFederate password used to authenticate to the PingFederate admin API when using basic authentication. | +| service.pingFederate.authentication.basicAuth.username | --pingfederate-username | PINGCLI_PINGFEDERATE_USERNAME | 14 | The PingFederate username used to authenticate to the PingFederate admin API when using basic authentication.

Example: 'administrator' | +| service.pingFederate.authentication.clientCredentialsAuth.clientID | --pingfederate-client-id | PINGCLI_PINGFEDERATE_CLIENT_ID | 14 | The PingFederate OAuth client ID used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. | +| service.pingFederate.authentication.clientCredentialsAuth.clientSecret | --pingfederate-client-secret | PINGCLI_PINGFEDERATE_CLIENT_SECRET | 14 | The PingFederate OAuth client secret used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. | +| service.pingFederate.authentication.clientCredentialsAuth.scopes | --pingfederate-scopes | PINGCLI_PINGFEDERATE_SCOPES | 15 | The PingFederate OAuth scopes used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. (default [])

Accepts a comma-separated string to delimit multiple scopes.

Example: 'openid,profile' | +| service.pingFederate.authentication.clientCredentialsAuth.tokenURL | --pingfederate-token-url | PINGCLI_PINGFEDERATE_TOKEN_URL | 14 | The PingFederate OAuth token URL used to authenticate to the PingFederate admin API when using the OAuth 2.0 client credentials grant type. | +| service.pingFederate.authentication.type | --pingfederate-authentication-type | PINGCLI_PINGFEDERATE_AUTHENTICATION_TYPE | 9 | The authentication type to use when connecting to the PingFederate admin API.

Options are: accessTokenAuth, basicAuth, clientCredentialsAuth.

Example: 'basicAuth' | +| service.pingFederate.caCertificatePEMFiles | --pingfederate-ca-certificate-pem-files | PINGCLI_PINGFEDERATE_CA_CERTIFICATE_PEM_FILES | 15 | Relative or full paths to PEM-encoded certificate files to be trusted as root CAs when connecting to the PingFederate server over HTTPS. (default [])

Accepts a comma-separated string to delimit multiple PEM files. | +| service.pingFederate.httpsHost | --pingfederate-https-host | PINGCLI_PINGFEDERATE_HTTPS_HOST | 14 | The PingFederate HTTPS host used to communicate with PingFederate's admin API.

Example: 'https://pingfederate-admin.bxretail.org' | +| service.pingFederate.insecureTrustAllTLS | --pingfederate-insecure-trust-all-tls | PINGCLI_PINGFEDERATE_INSECURE_TRUST_ALL_TLS | 0 | Trust any certificate when connecting to the PingFederate server admin API. (default false)

This is insecure and shouldn't be enabled outside of testing. | +| service.pingFederate.xBypassExternalValidationHeader | --pingfederate-x-bypass-external-validation-header | PINGCLI_PINGFEDERATE_X_BYPASS_EXTERNAL_VALIDATION_HEADER | 0 | Bypass connection tests when configuring PingFederate (the X-BypassExternalValidation header when using PingFederate's admin API). (default false) | +| service.pingOne.authentication.type | --pingone-authentication-type | PINGCLI_PINGONE_AUTHENTICATION_TYPE | 10 | The authentication type to use to authenticate to the PingOne management API. (default worker)

Options are: worker. | +| service.pingOne.authentication.worker.clientID | --pingone-worker-client-id | PINGCLI_PINGONE_WORKER_CLIENT_ID | 16 | The worker client ID used to authenticate to the PingOne management API. | +| service.pingOne.authentication.worker.clientSecret | --pingone-worker-client-secret | PINGCLI_PINGONE_WORKER_CLIENT_SECRET | 14 | The worker client secret used to authenticate to the PingOne management API. | +| service.pingOne.authentication.worker.environmentID | --pingone-worker-environment-id | PINGCLI_PINGONE_WORKER_ENVIRONMENT_ID | 16 | The ID of the PingOne environment that contains the worker client used to authenticate to the PingOne management API. | +| service.pingOne.regionCode | --pingone-region-code | PINGCLI_PINGONE_REGION_CODE | 11 | The region code of the PingOne tenant.

Options are: AP, AU, CA, EU, NA.

Example: 'NA' | + diff --git a/tools/generate-options-docs/main.go b/tools/generate-options-docs/main.go new file mode 100644 index 00000000..41af2f0b --- /dev/null +++ b/tools/generate-options-docs/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "flag" + "fmt" + "os" + "time" + + "github.com/pingidentity/pingcli/tools/docutil" + docgen "github.com/pingidentity/pingcli/tools/generate-options-docs/docgen" +) + +// A tiny standalone tool (invoked via `make generate-options-docs`) to output +// documentation for configuration options in either Markdown or AsciiDoc. +func main() { + outFile := flag.String("o", "", "Write output to file (extension .md or .adoc determines format unless flags override)") + asAsciiDoc := flag.Bool("asciidoc", false, "Force AsciiDoc output (default: Markdown unless output file has .adoc/.asciidoc)") + date := flag.String("date", time.Now().Format("January 2, 2006"), "Revision date to use if content changes (created-date preserved if file exists)") + flag.Parse() + + useAscii := docgen.ShouldOutputAsciiDoc(*outFile, *asAsciiDoc) + + var content string + if useAscii { + // Pull existing created-date if file already present so we can preserve it. + created := *date + if *outFile != "" { + if raw, err := os.ReadFile(*outFile); err == nil { + if prevCreated := docutil.ExtractDateLine(string(raw), ":created-date:"); prevCreated != "" { + created = prevCreated + } + } + } + content = docgen.GenerateAsciiDocWithDates(created, *date) + } else { + content = docgen.GenerateMarkdown() + } + + if *outFile == "" { + fmt.Print(content) + + return + } + + // Conditional write: only update if non-date content changed. + if oldRaw, err := os.ReadFile(*outFile); err == nil { + if docutil.NormalizeForCompare(string(oldRaw)) == docutil.NormalizeForCompare(content) { + // No meaningful change; avoid updating revision date line. + return + } + } + + if err := os.WriteFile(*outFile, []byte(content), 0o600); err != nil { + fmt.Fprintf(os.Stderr, "failed to write file: %v\n", err) + os.Exit(1) + } +}