-
Notifications
You must be signed in to change notification settings - Fork 49
RTECO-1648 - Implement jf agent apm command #518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
udaykb2
wants to merge
37
commits into
main
Choose a base branch
from
RTECO-1648-apm-support-implementation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 11 commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
2449217
RTECO-1648 - Implement jf agent apm command with JFrog Artifactory au…
udaykb2 9daad04
Merge branch 'main' into RTECO-1648-apm-support-implementation
udaykb2 ea04030
RTECO-1648 - Fix gosec findings in apm package (Go-Sec CI check)
udaykb2 859187e
RTECO-1648 - Bump jfrog-cli-core to the pushed RTECO-1648 commit
udaykb2 ff8d85e
RTECO-1648 - Drop direct-credential flags from apm, rename flagkit keys
udaykb2 0d839d2
RTECO-1648 - Replace regexp version parsing with a plain function
udaykb2 8f9e3b1
RTECO-1648 - Drop --server-id/--repo from apm, rename flagkit key, cl…
udaykb2 192cf73
RTECO-1648 - Fix apm.yml registries: block silently discarded with de…
udaykb2 700376f
Fix TestResolveRepoNameFromRegistry failing on Windows
udaykb2 f6e3616
Add AI help descriptions for APM commands (install, publish, update)
udaykb2 fabe9b1
Merge branch 'main' into RTECO-1648-apm-support-implementation
udaykb2 17333ab
RTECO-1648 - Replace AQL checksum lookup with HEAD, fix PR #518 revie…
udaykb2 f218bf8
Document apm passthrough capability and fix its --help handling
udaykb2 1d4c81e
Merge branch 'main' into RTECO-1648-apm-support-implementation
udaykb2 93293ae
Isolate AgentPackages const from the PackageTypes alignment group
udaykb2 e3d6c56
Add agent-apm entry to packageManagerConfigs
udaykb2 5dfbbf6
Fix 3 new CodeRabbit findings on PR #518
udaykb2 f28d7ac
Deduplicate repeated string literals and trim comments in apm code
udaykb2 40891e2
Fix apm publish artifact linkage in Artifactory build browser
udaykb2 727005d
Add comprehensive tests for BuildRegistryEntry and token generation
udaykb2 6964fe1
Fix APM access token generation: wrong endpoint and response field
udaykb2 db4844f
Detect APM validation failures that exit with code 0
udaykb2 86b9aa6
Resolve build-info repo name from --registry/default instead of host-…
udaykb2 bfdcddb
Unify apm install/publish build-info module IDs to name:version
udaykb2 dab7393
Fix data race sharing one HttpClientDetails across concurrent checksu…
udaykb2 cdbc586
Add local-zip fallback for publish checksum, matching cargo/ruby's pa…
udaykb2 5fd8b16
Fix build-info gaps in apm install/publish/update: dry-run, global,
udaykb2 5cc0db5
Merge branch 'main' into RTECO-1648-apm-support-implementation
udaykb2 e3b9a34
Give apm dependencies a single dev/prod/transitive scope, pnpm-style
udaykb2 04dcc73
Add real command examples to install/update help, matching publish's
udaykb2 20eccdf
Only log APM build-info skip messages when collection is enabled
udaykb2 207f450
Remove APM --global build-info skip special-casing
udaykb2 be0940c
Add apt command package from main for CLI compatibility
udaykb2 3a4ca0f
Merge main into RTECO-1648-apm-support-implementation
udaykb2 493ba9c
Remove separate folder for passthrough
udaykb2 2229d1c
RTECO-1648 - Bump min supported apm version to 0.23.0 and rename setu…
udaykb2 e2fe466
Merge branch 'main' into RTECO-1648-apm-support-implementation
udaykb2 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| package cli | ||
|
|
||
| import ( | ||
| "github.com/jfrog/jfrog-cli-artifactory/agent/apm/commands/install" | ||
| "github.com/jfrog/jfrog-cli-artifactory/agent/apm/commands/publish" | ||
| "github.com/jfrog/jfrog-cli-artifactory/agent/apm/commands/update" | ||
| "github.com/jfrog/jfrog-cli-artifactory/cliutils/flagkit" | ||
| "github.com/jfrog/jfrog-cli-core/v2/plugins/components" | ||
| ) | ||
|
|
||
| // GetSubCommands returns the leaf commands for `jf agent apm`. | ||
| // Commands not listed here fall through to the passthrough handler set on the parent. | ||
| // "lock" is deliberately not listed here — it doesn't deploy anything, so there's nothing | ||
| // a build actually consumed to report; it's served by the generic passthrough like every | ||
| // other read/resolve-only apm command. | ||
| func GetSubCommands() []components.Command { | ||
| return []components.Command{ | ||
| { | ||
| Name: "install", | ||
| Flags: flagkit.GetCommandFlags(flagkit.AgentApm), | ||
| // SkipFlagParsing so apm-native flags (e.g. --frozen) that aren't in jf's own | ||
| // declared flag set above aren't rejected by urfave/cli before reaching apm. | ||
| // RunInstall extracts jf's own flags manually via ExtractApmSubcommandOptions. | ||
| SkipFlagParsing: true, | ||
| Description: "Install APM packages with JFrog Artifactory authentication.", | ||
| AIDescription: install.GetAIDescription(), | ||
| Action: install.RunInstall, | ||
| }, | ||
| { | ||
| Name: "publish", | ||
| Flags: flagkit.GetCommandFlags(flagkit.AgentApm), | ||
| SkipFlagParsing: true, | ||
| Description: "Publish an APM package to JFrog Artifactory.", | ||
| AIDescription: publish.GetAIDescription(), | ||
| Action: publish.RunPublish, | ||
| }, | ||
| { | ||
| Name: "update", | ||
| Flags: flagkit.GetCommandFlags(flagkit.AgentApm), | ||
| SkipFlagParsing: true, | ||
| Description: "Refresh APM dependencies to their latest matching refs, with build-info collection.", | ||
| AIDescription: update.GetAIDescription(), | ||
| Action: update.RunUpdate, | ||
| }, | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package install | ||
|
|
||
| func GetDescription() string { | ||
| return "Install APM packages with JFrog Artifactory authentication." | ||
| } | ||
|
|
||
| func GetAIDescription() string { | ||
| return `Install packages declared in apm.yml with authenticated access to JFrog Artifactory registries. | ||
|
|
||
| When to use: | ||
| - Installing packages into an agent project that has apm.yml configured. | ||
| - Accessing private or curated packages from Artifactory via registry credentials. | ||
| - Collecting build-info about package dependencies in CI/CD pipelines. | ||
|
|
||
| Prerequisites: | ||
| - apm CLI (>= 0.1.0) installed and in PATH. | ||
| - A registry declared in apm.yml's registries: block or configured via jf setup agent-apm. | ||
| - Read permission on the source Artifactory agentpackages repository. | ||
|
|
||
| Common patterns: | ||
| $ jf agent apm install | ||
| $ jf agent apm install --build-name=my-build --build-number=1 | ||
|
|
||
| Build info: | ||
| - Enabled with --build-name and --build-number flags. | ||
| - Captures installed packages and their transitive dependencies. | ||
| - Published to Artifactory for traceability and compliance. | ||
|
|
||
| Environment: | ||
| - Credentials injected via APM_REGISTRY_TOKEN_<NAME>, APM_REGISTRY_USER_<NAME>, APM_REGISTRY_PASS_<NAME>. | ||
| - Registry configuration sourced from ~/.apm/config.json (set by jf setup agent-apm). | ||
| - Lockfile apm.lock.yaml created in working directory. | ||
|
|
||
| Related: jf agent apm publish, jf agent apm update, jf setup agent-apm` | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| package install | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "path/filepath" | ||
|
|
||
| apmcommon "github.com/jfrog/jfrog-cli-artifactory/agent/apm/common" | ||
| agentcommon "github.com/jfrog/jfrog-cli-artifactory/agent/common" | ||
| buildUtils "github.com/jfrog/jfrog-cli-core/v2/common/build" | ||
| "github.com/jfrog/jfrog-cli-core/v2/common/commands" | ||
| "github.com/jfrog/jfrog-cli-core/v2/plugins/components" | ||
| "github.com/jfrog/jfrog-cli-core/v2/utils/config" | ||
| "github.com/jfrog/jfrog-client-go/utils/log" | ||
| ) | ||
|
|
||
| // ApmInstallCommand runs `apm install` with JFrog Artifactory authentication and collects | ||
| // build-info from the resulting apm.lock.yaml. | ||
| // | ||
| // Unlike passthrough commands, install never accepts --repo: no other package-manager | ||
| // integration in this CLI supports declaring a new repository at run time either - they all | ||
| // require the one-time `jf setup <tool>` step first (§3, "jf setup agent-apm"). A registry | ||
| // must already be declared (via jf setup agent-apm or apm.yml's own registries: block) before | ||
| // install can authenticate against it. | ||
| type ApmInstallCommand struct { | ||
| args []string | ||
| serverDetails *config.ServerDetails | ||
| buildConfiguration *buildUtils.BuildConfiguration | ||
| } | ||
|
|
||
| func NewApmInstallCommand() *ApmInstallCommand { | ||
| return &ApmInstallCommand{} | ||
| } | ||
|
|
||
| func (c *ApmInstallCommand) SetArgs(args []string) *ApmInstallCommand { | ||
| c.args = args | ||
| return c | ||
| } | ||
|
|
||
| func (c *ApmInstallCommand) SetServerDetails(serverDetails *config.ServerDetails) *ApmInstallCommand { | ||
| c.serverDetails = serverDetails | ||
| return c | ||
| } | ||
|
|
||
| func (c *ApmInstallCommand) SetBuildConfiguration(buildConfiguration *buildUtils.BuildConfiguration) *ApmInstallCommand { | ||
| c.buildConfiguration = buildConfiguration | ||
| return c | ||
| } | ||
|
|
||
| func (c *ApmInstallCommand) CommandName() string { | ||
| return "rt_agent_apm_install" | ||
| } | ||
|
|
||
| func (c *ApmInstallCommand) ServerDetails() (*config.ServerDetails, error) { | ||
| return c.serverDetails, nil | ||
| } | ||
|
|
||
| func (c *ApmInstallCommand) Run() error { | ||
| log.Info("Running apm install...") | ||
|
|
||
| if err := apmcommon.RunApmSubcommandWithAuth("install", c.args, c.serverDetails); err != nil { | ||
| return fmt.Errorf("run apm install: %w", err) | ||
| } | ||
|
|
||
| workingDir, err := os.Getwd() | ||
| if err != nil { | ||
| log.Warn("apm install completed, but could not determine working directory for build info:", err.Error()) | ||
| } else { | ||
| lockfilePath := filepath.Join(workingDir, apmcommon.ApmLockfileName) | ||
| manifestPath := filepath.Join(workingDir, apmcommon.ApmManifestName) | ||
| if biErr := apmcommon.CollectAndSaveInstallBuildInfo(lockfilePath, manifestPath, c.serverDetails, c.buildConfiguration); biErr != nil { | ||
| log.Warn("apm install completed, but build info collection failed:", biErr.Error()) | ||
| } | ||
| } | ||
|
|
||
| log.Info("apm install finished successfully.") | ||
| return nil | ||
| } | ||
|
|
||
| // RunInstall is the CLI action handler for `jf agent apm install`. | ||
| func RunInstall(c *components.Context) error { | ||
| if apmcommon.IsHelpRequest(c.Arguments) { | ||
| return apmcommon.RunApmCommand(nil, "install", []string{"--help"}) | ||
| } | ||
|
|
||
| opts, err := apmcommon.ExtractApmSubcommandOptions(c.Arguments) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| serverDetails, err := agentcommon.GetServerDetails(c) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| cmd := NewApmInstallCommand(). | ||
| SetArgs(opts.RemainingArgs). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you mean by remaining args? it seems vague can you please change the name. |
||
| SetServerDetails(serverDetails). | ||
| SetBuildConfiguration(opts.BuildConfig) | ||
|
|
||
| return commands.ExecWithPackageManager(cmd, "agent-apm") | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| package passthrough | ||
|
|
||
| import ( | ||
| apmcommon "github.com/jfrog/jfrog-cli-artifactory/agent/apm/common" | ||
| agentcommon "github.com/jfrog/jfrog-cli-artifactory/agent/common" | ||
| "github.com/jfrog/jfrog-cli-core/v2/common/commands" | ||
| "github.com/jfrog/jfrog-cli-core/v2/plugins/components" | ||
| "github.com/jfrog/jfrog-cli-core/v2/utils/config" | ||
| "github.com/jfrog/jfrog-client-go/utils/log" | ||
| ) | ||
|
|
||
| // ApmPassthroughCommand forwards any apm subcommand with auth environment injected. | ||
| type ApmPassthroughCommand struct { | ||
| subcmd string | ||
| args []string | ||
| serverDetails *config.ServerDetails | ||
| } | ||
|
|
||
| func NewApmPassthroughCommand() *ApmPassthroughCommand { | ||
| return &ApmPassthroughCommand{} | ||
| } | ||
|
|
||
| func (c *ApmPassthroughCommand) SetSubcmd(subcmd string) *ApmPassthroughCommand { | ||
| c.subcmd = subcmd | ||
| return c | ||
| } | ||
|
|
||
| func (c *ApmPassthroughCommand) SetArgs(args []string) *ApmPassthroughCommand { | ||
| c.args = args | ||
| return c | ||
| } | ||
|
|
||
| func (c *ApmPassthroughCommand) SetServerDetails(serverDetails *config.ServerDetails) *ApmPassthroughCommand { | ||
| c.serverDetails = serverDetails | ||
| return c | ||
| } | ||
|
|
||
| func (c *ApmPassthroughCommand) CommandName() string { | ||
| return "rt_agent_apm_" + c.subcmd | ||
| } | ||
|
|
||
| func (c *ApmPassthroughCommand) ServerDetails() (*config.ServerDetails, error) { | ||
| return c.serverDetails, nil | ||
| } | ||
|
|
||
| func (c *ApmPassthroughCommand) Run() error { | ||
| log.Info("Running apm " + c.subcmd + "...") | ||
| return apmcommon.RunApmSubcommandWithAuth(c.subcmd, c.args, c.serverDetails) | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| // RunApmPassthroughDefault handles any `jf agent apm <subcmd>` where <subcmd> is not one of the | ||
| // registered subcommands (install/publish/update). The subcmd is the first element of | ||
| // c.Arguments; every remaining element is forwarded to apm untouched. Auth always comes from | ||
| // the default configured JFrog server - passthrough takes no flags of its own at all, so there's | ||
| // nothing to extract from c.Arguments. | ||
| func RunApmPassthroughDefault(c *components.Context) error { | ||
| if len(c.Arguments) == 0 { | ||
| return apmcommon.RunApmCommand(nil, "--help", nil) | ||
| } | ||
|
|
||
| subcmd := c.Arguments[0] | ||
| if apmcommon.IsHelpRequest([]string{subcmd}) { | ||
| return apmcommon.RunApmCommand(nil, "--help", nil) | ||
| } | ||
|
|
||
| serverDetails, err := agentcommon.GetServerDetails(c) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| cmd := NewApmPassthroughCommand(). | ||
| SetSubcmd(subcmd). | ||
| SetArgs(c.Arguments[1:]). | ||
| SetServerDetails(serverDetails) | ||
|
|
||
| return commands.ExecWithPackageManager(cmd, "agent-apm") | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package publish | ||
|
|
||
| func GetDescription() string { | ||
| return "Publish an APM package to JFrog Artifactory." | ||
| } | ||
|
|
||
| func GetAIDescription() string { | ||
| return `Publish an agent package to a JFrog Artifactory agentpackages repository with authenticated access. | ||
|
|
||
| When to use: | ||
| - Publishing custom agent packages for installation across multiple projects. | ||
| - Packaging skills, tools, or other agent extensions for organizational use. | ||
| - Creating reproducible, versioned deployments of agent components. | ||
|
|
||
| Prerequisites: | ||
| - apm CLI (>= 0.1.0) installed and in PATH. | ||
| - An apm.yml file in the package directory (or parent directories). | ||
| - Write permission on the Artifactory agentpackages repository. | ||
| - Registry configured via jf setup agent-apm or apm.yml's registries: block. | ||
|
|
||
| Common patterns: | ||
| $ jf agent apm publish my-org/my-package | ||
| $ jf agent apm publish my-org/my-package --build-name=my-build --build-number=1 | ||
| $ jf agent apm publish my-org/my-package --build-name=my-build --build-number=1 --module=my-module | ||
|
|
||
| Package format: | ||
| - Directory with apm.yml declaring name, version, and description. | ||
| - Optional skills/ subdirectory containing Cursor Agent Skills. | ||
| - Version in apm.yml becomes the published package version. | ||
|
|
||
| Build info: | ||
| - Enabled with --build-name and --build-number flags. | ||
| - Captures package metadata and publishing source. | ||
| - Published to Artifactory for traceability and compliance. | ||
| - Optional --module to group multiple packages in the same build. | ||
|
|
||
| Environment: | ||
| - Credentials injected via APM_REGISTRY_TOKEN_<NAME>, APM_REGISTRY_USER_<NAME>, APM_REGISTRY_PASS_<NAME>. | ||
| - Registry configuration sourced from ~/.apm/config.json (set by jf setup agent-apm). | ||
|
|
||
| Related: jf agent apm install, jf agent apm update, jf setup agent-apm` | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really need to support update command for build info collection? can you please give an example here why ?