From 3dbc349cc507a1f8d9e3b6419905c0d8df2679ba Mon Sep 17 00:00:00 2001 From: Jakub Bilski Date: Thu, 22 Feb 2024 08:29:23 +0000 Subject: [PATCH 1/9] DXE-3518 Update package-list.json --- CHANGELOG.md | 6 ++ pkg/commands/package_list/package-list.json | 98 ++++++++++++--------- 2 files changed, 64 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06ed466..0f0fb1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # RELEASE NOTES +## X.X.X (X X, X) + +### Fixes + +* Updated the versions, descriptions, requirements and packages of the dependencies in the `packages-list.json` ([GH#192](https://github.com/akamai/cli/issues/192)) + ## 1.5.6 (January 22, 2024) ### Enhancements diff --git a/pkg/commands/package_list/package-list.json b/pkg/commands/package_list/package-list.json index 200553c..463748e 100644 --- a/pkg/commands/package_list/package-list.json +++ b/pkg/commands/package_list/package-list.json @@ -46,20 +46,11 @@ { "title": "Application Security", "name": "appsec", - "version": "2.7.0", + "version": "2.8.0", "url": "https://github.com/akamai/cli-appsec", "issues": "https://github.com/akamai/cli-appsec/issues", - "commands": [{"name":"appsec","version":"2.7.0","description":"Akamai Security tools for protecting websites."}], - "requirements": {"node":"7.0.0"} - }, - { - "title": "Authentication", - "name": "auth", - "version": "0.0.3", - "url": "https://github.com/akamai/cli-auth", - "issues": "https://github.com/akamai/cli-auth/issues", - "commands": [{"name":"auth","version":"0.0.3","description":"Interface for Akamai Edgegrid Authentication","bin":"https://github.com/akamai/cli-auth/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}-{{.Arch}}{{.BinSuffix}}"}], - "requirements": {"node":"7.0.0"} + "commands": [{"name":"appsec","version":"2.8.0","description":"Akamai Security tools for protecting websites."}], + "requirements": {"node":"10.0.0"} }, { "title": "Client Access Control (CAC)", @@ -93,11 +84,11 @@ { "title": "Cloudlets", "name": "cloudlets", - "version": "v1.0.1", + "version": "v1.1.1", "url": "https://github.com/akamai/cli-cloudlets", "issues": "https://github.com/akamai/cli-cloudlets/issues", - "commands": [{"name":"cloudlets","aliases":["cloudlets"],"version":"1.0.1","description":"Manage Akamai Cloudlets"}], - "requirements": {"python":"3.0.0"} + "commands": [{"name":"cloudlets","aliases":["cloudlets"],"version":"1.1.1","description":"Manage Akamai Cloudlets"}], + "requirements": {"python":"3.6.0"} }, { "title": "Diagnostics", @@ -105,7 +96,16 @@ "version": "v1.1.0", "url": "https://github.com/akamai/cli-diagnostics", "issues": "https://github.com/akamai/cli-diagnostics/issues", - "commands": [{"name":"diagnostics","aliases":["diag", "edge-diagnostics"],"version":"v1.1.0","description":"Edge Diagnostics enables you to identify, analyze, and troubleshoot common content delivery network issues that your users may encounter.","bin": "https://github.com/akamai/cli-diagnostics/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}"}], + "commands": [ + { + "name":"diagnostics", + "aliases":["diag", "edge-diagnostics"], + "version":"v1.1.0", + "description":"Edge Diagnostics enables you to identify, analyze, and troubleshoot common content delivery network issues that your users may encounter.", + "bin": "https://github.com/akamai/cli-diagnostics/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}", + "auto-complete": true + } + ], "requirements": {"go":"1.17.1"} }, { @@ -118,7 +118,7 @@ {"name":"edgeworkers","aliases":["ew", "edgeworkers"],"version":"1.7.3","description":"Manage Akamai EdgeWorkers code bundles."}, {"name":"edgekv","aliases":["ekv", "edgekv"],"version":"1.7.3","description":"Manage Akamai EdgeKV database."} ], - "requirements": {"node":"7.0.0"} + "requirements": {"node":"14.0.0"} }, { "title": "Akamai Sandbox", @@ -141,13 +141,13 @@ { "title": "Enterprise Application Access", "name": "eaa", - "version": "0.5.7", + "version": "0.6.3", "url": "https://github.com/akamai/cli-eaa", "issues": "https://github.com/akamai/cli-eaa/issues", "commands": [ { "name": "eaa", - "version": "0.5.7", + "version": "0.6.3", "description": "Akamai CLI for Enterprise Application Access (EAA)" } ], @@ -158,7 +158,7 @@ { "title": "Firewall and Site Shield", "name": "firewall", - "version": "v0.2.1", + "version": "v0.2.2", "url": "https://github.com/akamai/cli-firewall", "issues": "https://github.com/akamai/cli-firewall/issues", "commands": [ @@ -184,14 +184,14 @@ "issues": "https://github.com/akamai/cli-image-manager/issues", "commands": [ {"name":"image-manager","aliases":["im"],"version":"0.1.9","description":"An Akamai CLI package for Image Manager"}, - {"name":"video-manager","aliases":["im"],"version":"0.1.9","description":"An Akamai CLI package for Video Manager"} + {"name":"video-manager","aliases":["vm"],"version":"0.1.9","description":"An Akamai CLI package for Video Manager"} ], "requirements": {"python":"3.0.0"} }, { "title": "Jsonnet", "name": "jsonnet", - "version": "0.8.0", + "version": "0.9.0", "url": "https://github.com/akamai/cli-jsonnet", "issues": "https://github.com/akamai/cli-jsonnet/issues", "commands": [ @@ -200,7 +200,7 @@ "aliases": [ "jsonnet" ], - "version": "0.8.0", + "version": "0.9.0", "description": "Utilities for managing Akamai as jsonnet" } ], @@ -220,21 +220,12 @@ { "title": "Onboard", "name": "onboard", - "version": "2.2.0", + "version": "2.3.1", "url": "https://github.com/akamai/cli-onboard", "issues": "https://github.com/akamai/cli-onboard/issues", - "commands": [{"name":"onboard","aliases":["onboard"],"version":"2.2.0","description":"Onboard Akamai delivery and WAF configuration"}], + "commands": [{"name":"onboard","aliases":["onboard"],"version":"2.3.1","description":"Onboard Akamai delivery and WAF configuration"}], "requirements": {"python":"3.6.0"} }, - { - "title": "Property Manager 1.0", - "name": "property", - "version": "1.1.6", - "url": "https://github.com/akamai/cli-property", - "issues": "https://github.com/akamai/cli-property/issues", - "commands": [{"name":"property","version":"1.1.6","description":"Manage configurations for Akamai properties","bin":"https://github.com/akamai/cli-property/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}-{{.Arch}}{{.BinSuffix}}"}], - "requirements": {"node":"7.0.0"} - }, { "title": "Property Manager", "name": "property-manager", @@ -242,8 +233,8 @@ "url": "https://github.com/akamai/cli-property-manager", "issues": "https://github.com/akamai/cli-property-manager/issues", "commands": [ - {"name":"snippets","aliases":["pm","property-manager"],"version":"0.7.8-RELEASE","description":"Property Manager CLI for DevOps"}, - {"name":"pipeline","aliases":["pl","pipeline","pd","proddeploy"],"version":"0.7.8-RELEASE","description":"Akamai Pipeline for DevOps"} + {"name":"property-manager","aliases":["pm","snippets"],"version":"0.7.8-RELEASE","description":"Property Manager CLI for DevOps"}, + {"name":"pipeline","aliases":["pl","pd","proddeploy"],"version":"0.7.8-RELEASE","description":"Akamai Pipeline for DevOps"} ], "requirements": {"node":"8.9.1"} }, @@ -259,22 +250,22 @@ { "title": "Terraform Client Configuration", "name": "cli terraform", - "version": "1.5.0", + "version": "1.12.0", "url": "https://github.com/akamai/cli-terraform", "issues": "https://github.com/akamai/cli-terraform/issues", - "commands": [{"name":"terraform","version":"1.5.0","description":"Administer and Manage Akamai Terraform configurations","bin":"https://github.com/akamai/cli-terraform/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}","auto-complete":true,"ldflags": "-X 'github.com/akamai/cli-terraform/cli.Version=%s'"}], + "commands": [{"name":"terraform","version":"1.12.0","description":"Administer and Manage Akamai Terraform configurations","bin":"https://github.com/akamai/cli-terraform/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}","auto-complete":true,"ldflags": "-X 'github.com/akamai/cli-terraform/cli.Version=%s'"}], "requirements": {"go":"1.18.0"} }, { "title": "Test Center", "name": "test-center", - "version": "0.2.0", + "version": "1.0.0", "url": "https://github.com/akamai/cli-test-center", "issues": "https://github.com/akamai/cli-test-center/issues", "commands": [ { "name": "test-center", - "version": "0.2.0", + "version": "1.0.0", "description": "Test Center is a testing tool that checks the effect of configuration changes on your web property. Use this tool as part of your testing protocol to increase your confidence in the safety and accuracy of your configuration changes.", "bin": "https://github.com/akamai/cli-test-center/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}" } @@ -289,6 +280,33 @@ "issues": "https://github.com/akamai/cli-visitor-prioritization/issues", "commands": [{"name":"visitor-prioritization","aliases":["vp"],"version":"0.3.0","description":"Access and control Visitor Prioritization cloudlet"}], "requirements": {"python":"3.0.0"} + }, + { + "title": "Enterprise Threat Protector", + "name": "etp", + "version": "v0.4.5", + "url": "https://github.com/akamai/cli-etp", + "issues": "https://github.com/akamai/cli-etp/issues", + "commands": [{"name":"etp","version":"v0.4.5", "description":"Akamai CLI for Secure Internet Access Enterprise (f.k.a. Enterprise Threat Protector)"}], + "requirements": {"python":"3.6.0"} + }, + { + "title": "Akamai MFA", + "name": "mfa", + "version": "v0.1.1", + "url": "https://github.com/akamai/cli-mfa", + "issues": "https://github.com/akamai/cli-mfa/issues", + "commands": [{"name":"mfa", "version":"v0.1.1", "description":"Akamai CLI for Akamai MFA"}], + "requirements": {"python":"3.7.0"} + }, + { + "title": "mPulse", + "name": "mpulse", + "version": "v0.2.0", + "url": "https://github.com/akamai/cli-mpulse", + "issues": "https://github.com/akamai/cli-mpulse/issues", + "commands": [{"name":"mpulse", "aliases": ["mp"], "version":"v0.2.0", "description":"Get mPulse reports for your applications"}], + "requirements": {"python":"3.0.0"} } ] } From f54329d52ee13ea4c97c86ac3d1c184edc541639 Mon Sep 17 00:00:00 2001 From: Filip Antkowiak Date: Mon, 4 Mar 2024 10:36:11 +0000 Subject: [PATCH 2/9] DXE-3348 Migrate code to go 1.21 version --- CHANGELOG.md | 4 ++++ README.md | 2 +- go.mod | 2 +- go.sum | 12 ++++++++++++ pkg/commands/package_list/package-list.json | 2 +- 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f0fb1c..514bbe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## X.X.X (X X, X) +### Enhancements + +* Migrated to go 1.21 + ### Fixes * Updated the versions, descriptions, requirements and packages of the dependencies in the `packages-list.json` ([GH#192](https://github.com/akamai/cli/issues/192)) diff --git a/README.md b/README.md index 8b9edf2..609b6f4 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ This command installs the CLI and persists the configuration and packages in `$H ### Compile from Source -**Prerequisite:** Make sure you install Go 1.17 or later. +**Prerequisite:** Make sure you install Go 1.21 or later. To compile Akamai CLI from source: diff --git a/go.mod b/go.mod index 6f144ad..080bae3 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/akamai/cli -go 1.20 +go 1.21 require ( github.com/AlecAivazis/survey/v2 v2.3.5 diff --git a/go.sum b/go.sum index 4a758af..a1df7de 100644 --- a/go.sum +++ b/go.sum @@ -13,12 +13,14 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDe github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4Pt2A= @@ -37,6 +39,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -44,11 +47,13 @@ github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU= @@ -59,6 +64,7 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -83,9 +89,11 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= @@ -101,6 +109,7 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -110,6 +119,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -180,6 +190,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -230,6 +241,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/pkg/commands/package_list/package-list.json b/pkg/commands/package_list/package-list.json index 463748e..579f27b 100644 --- a/pkg/commands/package_list/package-list.json +++ b/pkg/commands/package_list/package-list.json @@ -254,7 +254,7 @@ "url": "https://github.com/akamai/cli-terraform", "issues": "https://github.com/akamai/cli-terraform/issues", "commands": [{"name":"terraform","version":"1.12.0","description":"Administer and Manage Akamai Terraform configurations","bin":"https://github.com/akamai/cli-terraform/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}","auto-complete":true,"ldflags": "-X 'github.com/akamai/cli-terraform/cli.Version=%s'"}], - "requirements": {"go":"1.18.0"} + "requirements": {"go":"1.21.0"} }, { "title": "Test Center", From 6cbe35753ecfccbec3d9b98cd376b29ae1c74611 Mon Sep 17 00:00:00 2001 From: Lukasz Sadlon Date: Fri, 12 Apr 2024 10:10:46 +0000 Subject: [PATCH 3/9] DXE-1865 Dependency update Merge in DEVEXP/cli from feature/DXE-1865-resolve-cli-vulnerabilities to develop --- CHANGELOG.md | 1 + go.mod | 2 +- go.sum | 6 ++---- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 514bbe2..afe24fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Enhancements * Migrated to go 1.21 +* Updated various dependencies ### Fixes diff --git a/go.mod b/go.mod index 080bae3..c366db0 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/akamai/cli go 1.21 require ( - github.com/AlecAivazis/survey/v2 v2.3.5 + github.com/AlecAivazis/survey/v2 v2.3.7 github.com/Masterminds/semver v1.5.0 github.com/apex/log v1.9.0 github.com/briandowns/spinner v1.23.0 diff --git a/go.sum b/go.sum index a1df7de..30990b8 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ= -github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= +github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= +github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= @@ -202,7 +202,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -213,7 +212,6 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= From 71ae598725e34088a6edbbccac73a66ab303eac0 Mon Sep 17 00:00:00 2001 From: Filip Antkowiak Date: Tue, 21 May 2024 08:24:34 +0000 Subject: [PATCH 4/9] DXE-3781 Change the order in cli package instalation --- CHANGELOG.md | 4 +- pkg/commands/command.go | 4 +- pkg/commands/command_install.go | 139 +++++---- pkg/commands/command_install_test.go | 286 +++++++++++------- pkg/commands/command_subcommand.go | 2 +- pkg/commands/command_update.go | 95 +++++- pkg/commands/command_update_test.go | 123 +++++++- pkg/commands/helpers_test.go | 52 ++++ pkg/commands/subcommands.go | 46 ++- .../.akamai-cli/src/cli-echo-python/cli.json | 3 +- .../.akamai-cli/src/cli-echo/cli.json | 3 +- .../.akamai-cli/src/cli-installed/cli.json | 3 +- 12 files changed, 564 insertions(+), 196 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afe24fc..db436e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,9 @@ ## X.X.X (X X, X) ### Enhancements - +* Changed package installation order + * Cli will first check if new binaries are available, if the package has no binaries or no valid binaries can be found, it will build the package locally. + * --force flag has been deprecated for both install and update * Migrated to go 1.21 * Updated various dependencies diff --git a/pkg/commands/command.go b/pkg/commands/command.go index 7d61575..782d258 100644 --- a/pkg/commands/command.go +++ b/pkg/commands/command.go @@ -209,7 +209,7 @@ func createBuiltinCommands() []*cli.Command { Flags: []cli.Flag{ &cli.BoolFlag{ Name: "force", - Usage: "Force binary installation if available when source installation fails", + Usage: "[deprecated] Force binary installation if available when source installation fails", }, }, HideHelp: true, @@ -254,7 +254,7 @@ func createBuiltinCommands() []*cli.Command { Flags: []cli.Flag{ &cli.BoolFlag{ Name: "force", - Usage: "Force binary installation if available when source installation fails", + Usage: "[deprecated] Force binary installation if available when source installation fails", }, }, HideHelp: true, diff --git a/pkg/commands/command_install.go b/pkg/commands/command_install.go index 3eec5de..788c572 100644 --- a/pkg/commands/command_install.go +++ b/pkg/commands/command_install.go @@ -34,6 +34,7 @@ import ( var ( thirdPartyDisclaimer = color.CyanString("Disclaimer: You are installing a third-party package, subject to its own terms and conditions. Akamai makes no warranty or representation with respect to the third-party package.") + githubRawURLTemplate = "https://raw.githubusercontent.com/akamai/%s/master/cli.json" ) func cmdInstall(git git.Repository, langManager packages.LangManager) cli.ActionFunc { @@ -62,7 +63,7 @@ func cmdInstall(git git.Repository, langManager packages.LangManager) cli.Action for _, repo := range c.Args().Slice() { repo = tools.Githubize(repo) - subCmd, err := installPackage(c.Context, git, langManager, repo, c.Bool("force")) + subCmd, err := installPackage(c.Context, git, langManager, repo) if err != nil { return err @@ -124,7 +125,7 @@ func packageListDiff(c *cli.Context, oldcmds []subcommands) { listInstalledCommands(c, added, removed) } -func installPackage(ctx context.Context, gitRepo git.Repository, langManager packages.LangManager, repo string, forceBinary bool) (*subcommands, error) { +func installPackage(ctx context.Context, gitRepo git.Repository, langManager packages.LangManager, repo string) (*subcommands, error) { logger := log.FromContext(ctx) srcPath, err := tools.GetAkamaiCliSrcPath() if err != nil { @@ -132,19 +133,48 @@ func installPackage(ctx context.Context, gitRepo git.Repository, langManager pac } term := terminal.Get(ctx) - spin := term.Spinner() - spin.Start("Attempting to fetch command from %s...", repo) - dirName := strings.TrimSuffix(filepath.Base(repo), ".git") packageDir := filepath.Join(srcPath, dirName) + if _, err = os.Stat(packageDir); err == nil { - spin.Stop(terminal.SpinnerStatusWarn) warningMsg := fmt.Sprintf("Package directory already exists (%s). To reinstall this package, first run 'akamai uninstall' command.", packageDir) return nil, cli.Exit(color.YellowString(warningMsg), 0) } + spin.Start("Attempting to fetch package configuration from %s...", repo) + + base := filepath.Base(dirName) + url := fmt.Sprintf(githubRawURLTemplate, base) + cmdPackage, err := readPackageFromGithub(url, dirName) + if err != nil { + spin.Stop(terminal.SpinnerStatusFail) + logger.Error(err.Error()) + if _, err := term.Writeln(err.Error()); err != nil { + term.WriteError(err.Error()) + } + return nil, cli.Exit("Unable to install selected package", 1) + } + spin.OK() + + if isBinary(cmdPackage) { + + ok, subCmd := installPackageBinaries(ctx, packageDir, cmdPackage, logger) + if ok { + return subCmd, nil + } + // delete package directory + if err := os.RemoveAll(packageDir); err != nil { + return nil, err + } + + } + spin.Start("Attempting to fetch command from %s...", repo) + + if !strings.HasPrefix(repo, "https://github.com/akamai/cli-") && !strings.HasPrefix(repo, "git@github.com:akamai/cli-") { + term.Printf(color.CyanString(thirdPartyDisclaimer)) + } err = gitRepo.Clone(ctx, packageDir, repo, false, spin) if err != nil { if err := os.RemoveAll(packageDir); err != nil { @@ -157,11 +187,7 @@ func installPackage(ctx context.Context, gitRepo git.Repository, langManager pac } spin.OK() - if !strings.HasPrefix(repo, "https://github.com/akamai/cli-") && !strings.HasPrefix(repo, "git@github.com:akamai/cli-") { - term.Printf(color.CyanString(thirdPartyDisclaimer)) - } - - ok, subCmd := installPackageDependencies(ctx, langManager, packageDir, forceBinary, logger) + ok, subCmd := installPackageDependencies(ctx, langManager, packageDir, logger) if !ok { if err := os.RemoveAll(packageDir); err != nil { return nil, err @@ -172,7 +198,7 @@ func installPackage(ctx context.Context, gitRepo git.Repository, langManager pac return subCmd, nil } -func installPackageDependencies(ctx context.Context, langManager packages.LangManager, dir string, forceBinary bool, logger log.Logger) (bool, *subcommands) { +func installPackageDependencies(ctx context.Context, langManager packages.LangManager, dir string, logger log.Logger) (bool, *subcommands) { cmdPackage, err := readPackage(dir) term := terminal.Get(ctx) @@ -209,70 +235,57 @@ func installPackageDependencies(ctx context.Context, langManager packages.LangMa return true, &cmdPackage } - if err == nil { - term.Spinner().OK() - return true, &cmdPackage + if err != nil { + term.Spinner().Stop(terminal.SpinnerStatusFail) + term.WriteError(err.Error()) + return false, nil + } - first := true - for _, cmd := range cmdPackage.Commands { - if cmd.Bin != "" { - if first { - first = false - term.Spinner().Stop(terminal.SpinnerStatusWarn) - if _, err := term.Writeln(color.CyanString(err.Error())); err != nil { - term.WriteError(err.Error()) - return false, nil - } - logger.Warn(err.Error()) - if !forceBinary { - if !term.IsTTY() { - return false, nil - } - - answer, err := term.Confirm("Binary command(s) found, would you like to download and install it?", true) - if err != nil { - term.WriteError(err.Error()) - logger.Error(err.Error()) - return false, nil - } - - if !answer { - return false, nil - } - } + term.Spinner().OK() + return true, &cmdPackage - if err := os.MkdirAll(filepath.Join(dir, "bin"), 0700); err != nil { - return false, nil - } +} - term.Spinner().Start("Downloading binary...") - } +func installPackageBinaries(ctx context.Context, dir string, cmdPackage subcommands, logger log.Logger) (bool, *subcommands) { - if err = downloadBin(ctx, filepath.Join(dir, "bin"), cmd); err != nil { - term.Spinner().Stop(terminal.SpinnerStatusFail) - errorMsg := "Unable to download binary: " + err.Error() - if _, err := term.Writeln(color.RedString(errorMsg)); err != nil { - term.WriteError(err.Error()) - return false, nil - } - logger.Error(errorMsg) - return false, nil - } - } + term := terminal.Get(ctx) + spin := term.Spinner() + spin.Start("Installing Binaries...") - if first { - term.Spinner().Stop(terminal.SpinnerStatusFail) - if _, err := term.Writeln(color.RedString(err.Error())); err != nil { + if err := os.MkdirAll(filepath.Join(dir, "bin"), 0700); err != nil { + return false, nil + } + + for _, cmd := range cmdPackage.Commands { + err := downloadBin(ctx, filepath.Join(dir, "bin"), cmd) + if err != nil { + warnMsg := fmt.Sprintf("Unable to download binary: %v", err.Error()) + spin.Stop(terminal.SpinnerStatusWarn) + if _, err := term.Writeln(color.YellowString(warnMsg)); err != nil { term.WriteError(err.Error()) return false, nil } - logger.Error(err.Error()) + logger.Warn(warnMsg) + return false, nil + } } - term.Spinner().Stop(terminal.SpinnerStatusOK) + err := os.WriteFile(filepath.Join(dir, "cli.json"), cmdPackage.raw, 0644) + if err != nil { + spin.Stop(terminal.SpinnerStatusWarn) + warnMsg := "Unable to save configuration file " + err.Error() + if _, err := term.Writeln(color.YellowString(warnMsg)); err != nil { + term.WriteError(err.Error()) + return false, nil + } + logger.Warn(warnMsg) + return false, nil + } + spin.OK() return true, &cmdPackage + } diff --git a/pkg/commands/command_install_test.go b/pkg/commands/command_install_test.go index 7fecf40..b8ecc57 100644 --- a/pkg/commands/command_install_test.go +++ b/pkg/commands/command_install_test.go @@ -25,25 +25,40 @@ import ( func TestCmdInstall(t *testing.T) { cliTestCmdRepo := filepath.Join(".", "testdata", ".akamai-cli", "src", "cli-test-cmd") cliJSON := filepath.Join(".", "testdata", "repo", "cli.json") + cliInvalidJSON := filepath.Join(".", "testdata", "repo_invalid_json", "cli.json") + cliNoBinaryJSON := filepath.Join(".", "testdata", "repo_no_binary", "cli.json") + cliTestCmdJSON := filepath.Join(".", "testdata", ".akamai-cli", "src", "cli-test-cmd", "cli.json") cliTestInvalidJSONRepo := filepath.Join(".", "testdata", ".akamai-cli", "src", "cli-test-invalid-json") tests := map[string]struct { args []string - init func(*testing.T, *mocked) + init func(*testing.T, *mocked, *httptest.Server) teardown func(*testing.T) binaryResponseStatus int withError string }{ "install from official akamai repository, build from source": { args: []string{"test-cmd"}, - init: func(t *testing.T, m *mocked) { + init: func(t *testing.T, m *mocked, h *httptest.Server) { + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Attempting to fetch package configuration from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + + h = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + configJSON, err := os.ReadFile(cliNoBinaryJSON) + require.NoError(t, err) + _, err = w.Write(configJSON) + require.NoError(t, err) + })) + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + m.term.On("OK").Return().Once() m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), "https://github.com/akamai/cli-test-cmd.git", false, m.term).Return(nil).Once(). Run(func(args mock.Arguments) { - mustCopyFile(t, cliJSON, cliTestCmdRepo) + mustCopyFile(t, cliNoBinaryJSON, cliTestCmdRepo) }) m.term.On("OK").Return().Once() m.term.On("Spinner").Return(m.term).Once() @@ -69,10 +84,21 @@ func TestCmdInstall(t *testing.T) { }, "install from official akamai repository, build from source + ldflags": { args: []string{"test-cmd"}, - init: func(t *testing.T, m *mocked) { + init: func(t *testing.T, m *mocked, h *httptest.Server) { m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Attempting to fetch package configuration from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + + h = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + configJSON, err := os.ReadFile(cliNoBinaryJSON) + require.NoError(t, err) + _, err = w.Write(configJSON) + require.NoError(t, err) + })) + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + m.term.On("OK").Return().Once() m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), "https://github.com/akamai/cli-test-cmd.git", false, m.term).Return(nil).Once(). Run(func(args mock.Arguments) { @@ -102,36 +128,26 @@ func TestCmdInstall(t *testing.T) { }, "install from official akamai repository, download binary": { args: []string{"test-cmd"}, - init: func(t *testing.T, m *mocked) { + init: func(t *testing.T, m *mocked, h *httptest.Server) { m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Attempting to fetch package configuration from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + + h = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + configJSON, err := os.ReadFile(cliJSON) + output := strings.ReplaceAll(string(configJSON), "${REPOSITORY_URL}", os.Getenv("REPOSITORY_URL")) + require.NoError(t, err) + _, err = w.Write([]byte(output)) + require.NoError(t, err) + + })) + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() - m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), - "https://github.com/akamai/cli-test-cmd.git", false, m.term).Return(nil).Once(). - Run(func(args mock.Arguments) { - mustCopyFile(t, cliJSON, cliTestCmdRepo) - input, err := ioutil.ReadFile(cliTestCmdJSON) - require.NoError(t, err) - output := strings.ReplaceAll(string(input), "${REPOSITORY_URL}", os.Getenv("REPOSITORY_URL")) - err = ioutil.WriteFile(cliTestCmdJSON, []byte(output), 0755) - require.NoError(t, err) - }) - m.term.On("Spinner").Return(m.term).Once() m.term.On("OK").Return().Once() - m.term.On("Spinner").Return(m.term).Once() - m.term.On("Start", "Installing...", []interface{}(nil)).Return().Once() - - m.langManager.On("Install", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), - packages.LanguageRequirements{Go: "1.14.0"}, []string{"app-1-cmd-1"}, []string{""}).Return(fmt.Errorf("oops")).Once() - m.term.On("Spinner").Return(m.term).Once() m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() - m.term.On("Stop", terminal.SpinnerStatusWarn).Return().Once() - m.term.On("Writeln", []interface{}{color.CyanString("oops")}).Return(0, nil).Once() - m.term.On("IsTTY").Return(true).Once() - m.term.On("Confirm", "Binary command(s) found, would you like to download and install it?", true).Return(true, nil).Once() - m.term.On("Spinner").Return(m.term).Once() - m.term.On("Start", "Downloading binary...", []interface{}(nil)).Return().Once() + m.term.On("Spinner").Return(m.term).Once() - m.term.On("Stop", terminal.SpinnerStatusOK).Return().Once() + m.term.On("Start", "Installing Binaries...", []interface{}(nil)).Return().Once() + m.term.On("OK").Return().Once() // list all packages m.term.On("Printf", mock.AnythingOfType("string"), mock.Anything).Return() @@ -145,7 +161,7 @@ func TestCmdInstall(t *testing.T) { }, "package directory already exists": { args: []string{"installed"}, - init: func(t *testing.T, m *mocked) { + init: func(t *testing.T, m *mocked, h *httptest.Server) { m.term.On("Spinner").Return(m.term).Once() m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-installed.git"}).Return().Once() m.term.On("Stop", terminal.SpinnerStatusWarn).Return().Once() @@ -154,14 +170,25 @@ func TestCmdInstall(t *testing.T) { }, "no args passed": { args: []string{}, - init: func(t *testing.T, m *mocked) {}, + init: func(t *testing.T, m *mocked, h *httptest.Server) {}, withError: "You must specify a repository URL", }, "git clone error": { args: []string{"test-cmd"}, - init: func(t *testing.T, m *mocked) { + init: func(t *testing.T, m *mocked, h *httptest.Server) { m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Attempting to fetch package configuration from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + + h = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + configJSON, err := os.ReadFile(cliNoBinaryJSON) + require.NoError(t, err) + _, err = w.Write(configJSON) + require.NoError(t, err) + })) + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + m.term.On("OK").Return().Once() + m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), "https://github.com/akamai/cli-test-cmd.git", false, m.term).Return(git.ErrPackageNotAvailable).Once(). @@ -173,11 +200,23 @@ func TestCmdInstall(t *testing.T) { }, withError: "Package is not available. Supported packages can be found here: https://techdocs.akamai.com/home/page/products-tools-a-z", }, - "error reading downloaded package, invalid cli.json": { + "error reading downloaded package, invalid cli.json in repository": { args: []string{"test-invalid-json"}, - init: func(t *testing.T, m *mocked) { + init: func(t *testing.T, m *mocked, h *httptest.Server) { m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Attempting to fetch package configuration from %s...", []interface{}{"https://github.com/akamai/cli-test-invalid-json.git"}).Return().Once() + + h = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + configJSON, err := os.ReadFile(cliNoBinaryJSON) + require.NoError(t, err) + _, err = w.Write(configJSON) + require.NoError(t, err) + })) + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-invalid-json.git"}).Return().Once() + m.term.On("OK").Return().Once() + m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-invalid-json"), "https://github.com/akamai/cli-test-invalid-json.git", false, m.term).Return(nil).Once(). Run(func(args mock.Arguments) { @@ -198,63 +237,62 @@ func TestCmdInstall(t *testing.T) { }, withError: "Unable to install selected package", }, - "install from official akamai repository, unknown lang": { - args: []string{"test-cmd"}, - init: func(t *testing.T, m *mocked) { - m.term.On("Spinner").Return(m.term).Once() - m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() - m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), - "https://github.com/akamai/cli-test-cmd.git", false, m.term).Return(nil).Once(). - Run(func(args mock.Arguments) { - mustCopyFile(t, cliJSON, cliTestCmdRepo) - }) - m.term.On("Spinner").Return(m.term).Once() - m.term.On("OK").Return().Once() + "error reading downloaded package, invalid cli.json": { + args: []string{"test-invalid-json"}, + init: func(t *testing.T, m *mocked, h *httptest.Server) { m.term.On("Spinner").Return(m.term).Once() - m.term.On("Start", "Installing...", []interface{}(nil)).Return().Once() + m.term.On("Start", "Attempting to fetch package configuration from %s...", []interface{}{"https://github.com/akamai/cli-test-invalid-json.git"}).Return().Once() + h = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + configJSON, err := os.ReadFile(cliInvalidJSON) + require.NoError(t, err) + _, err = w.Write(configJSON) + require.NoError(t, err) + })) + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" + m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-invalid-json.git"}).Return().Once() + m.term.On("OK").Return().Once() m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() - m.langManager.On("Install", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), - packages.LanguageRequirements{Go: "1.14.0"}, []string{"app-1-cmd-1"}, []string{""}).Return(packages.ErrUnknownLang).Once() - m.term.On("Spinner").Return(m.term).Once() - m.term.On("WarnOK").Return().Once() - // list all packages - m.term.On("Printf", mock.AnythingOfType("string"), mock.Anything).Return() m.term.On("Writeln", mock.Anything).Return(0, nil) }, teardown: func(t *testing.T) { - require.NoError(t, os.RemoveAll(cliTestCmdRepo)) + require.NoError(t, os.RemoveAll(cliTestInvalidJSONRepo)) }, + withError: "Unable to install selected package", }, - "install from official akamai repository, user does not install binary": { + "install from official akamai repository, unknown lang": { args: []string{"test-cmd"}, - init: func(t *testing.T, m *mocked) { + init: func(t *testing.T, m *mocked, h *httptest.Server) { m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Attempting to fetch package configuration from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + + h = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + configJSON, err := os.ReadFile(cliNoBinaryJSON) + require.NoError(t, err) + _, err = w.Write(configJSON) + require.NoError(t, err) + })) + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + m.term.On("OK").Return().Once() + m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), "https://github.com/akamai/cli-test-cmd.git", false, m.term).Return(nil).Once(). Run(func(args mock.Arguments) { - mustCopyFile(t, cliJSON, cliTestCmdRepo) - input, err := ioutil.ReadFile(cliTestCmdJSON) - require.NoError(t, err) - output := strings.ReplaceAll(string(input), "${REPOSITORY_URL}", os.Getenv("REPOSITORY_URL")) - err = ioutil.WriteFile(cliTestCmdJSON, []byte(output), 0755) - require.NoError(t, err) + mustCopyFile(t, cliNoBinaryJSON, cliTestCmdRepo) }) m.term.On("Spinner").Return(m.term).Once() m.term.On("OK").Return().Once() m.term.On("Spinner").Return(m.term).Once() m.term.On("Start", "Installing...", []interface{}(nil)).Return().Once() + m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() m.langManager.On("Install", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), - packages.LanguageRequirements{Go: "1.14.0"}, []string{"app-1-cmd-1"}, []string{""}).Return(fmt.Errorf("oops")).Once() + packages.LanguageRequirements{Go: "1.14.0"}, []string{"app-1-cmd-1"}, []string{""}).Return(packages.ErrUnknownLang).Once() m.term.On("Spinner").Return(m.term).Once() - m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() - m.term.On("Stop", terminal.SpinnerStatusWarn).Return().Once() - m.term.On("Writeln", []interface{}{color.CyanString("oops")}).Return(0, nil).Once() - m.term.On("IsTTY").Return(true).Once() - m.term.On("Confirm", "Binary command(s) found, would you like to download and install it?", true).Return(false, nil).Once() + m.term.On("WarnOK").Return().Once() // list all packages m.term.On("Printf", mock.AnythingOfType("string"), mock.Anything).Return() @@ -263,92 +301,123 @@ func TestCmdInstall(t *testing.T) { teardown: func(t *testing.T) { require.NoError(t, os.RemoveAll(cliTestCmdRepo)) }, - withError: "Unable to install selected package", }, - "install from official akamai repository, error downloading binary, invalid URL": { + "install from official akamai repository, error downloading binary, invalid URL, build from source": { args: []string{"test-cmd"}, - init: func(t *testing.T, m *mocked) { + init: func(t *testing.T, m *mocked, h *httptest.Server) { m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Attempting to fetch package configuration from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + + h = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + configJSON, err := os.ReadFile(cliJSON) + output := strings.ReplaceAll(string(configJSON), "${REPOSITORY_URL}", "invalid url") + require.NoError(t, err) + _, err = w.Write([]byte(output)) + require.NoError(t, err) + + })) + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" + m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + m.term.On("OK").Return().Once() + m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Installing Binaries...", []interface{}(nil)).Return().Once() + m.term.On("Stop", terminal.SpinnerStatusWarn) + m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), "https://github.com/akamai/cli-test-cmd.git", false, m.term).Return(nil).Once(). Run(func(args mock.Arguments) { mustCopyFile(t, cliJSON, cliTestCmdRepo) }) - m.term.On("Spinner").Return(m.term).Once() + m.term.On("OK").Return().Once() m.term.On("Spinner").Return(m.term).Once() m.term.On("Start", "Installing...", []interface{}(nil)).Return().Once() m.langManager.On("Install", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), - packages.LanguageRequirements{Go: "1.14.0"}, []string{"app-1-cmd-1"}, []string{""}).Return(fmt.Errorf("oops")).Once() - m.term.On("Spinner").Return(m.term).Once() - m.term.On("Stop", terminal.SpinnerStatusWarn).Return().Once() - m.term.On("Writeln", []interface{}{color.CyanString("oops")}).Return(0, nil).Once() - m.term.On("IsTTY").Return(true).Once() - m.term.On("Confirm", "Binary command(s) found, would you like to download and install it?", true).Return(true, nil).Once() - m.term.On("Spinner").Return(m.term).Once() - m.term.On("Start", "Downloading binary...", []interface{}(nil)).Return().Once() + packages.LanguageRequirements{Go: "1.14.0"}, []string{"app-1-cmd-1"}, []string{""}).Return(nil).Once() m.term.On("Spinner").Return(m.term).Once() - m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + m.term.On("OK").Return().Once() // list all packages m.term.On("Printf", mock.AnythingOfType("string"), mock.Anything).Return() m.term.On("Writeln", mock.Anything).Return(0, nil) }, binaryResponseStatus: http.StatusOK, - withError: "Unable to install selected package", teardown: func(t *testing.T) { require.NoError(t, os.RemoveAll(cliTestCmdRepo)) }, }, - "install from official akamai repository, error downloading binary, invalid response status": { + "install from official akamai repository, error downloading binary, invalid response status, build from source": { args: []string{"test-cmd"}, - init: func(t *testing.T, m *mocked) { + init: func(t *testing.T, m *mocked, h *httptest.Server) { m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Attempting to fetch package configuration from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + + h = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + configJSON, err := os.ReadFile(cliJSON) + output := strings.ReplaceAll(string(configJSON), "${REPOSITORY_URL}", "invalid url") + require.NoError(t, err) + _, err = w.Write([]byte(output)) + require.NoError(t, err) + + })) + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" + m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + m.term.On("OK").Return().Once() + m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Installing Binaries...", []interface{}(nil)).Return().Once() + m.term.On("Stop", terminal.SpinnerStatusWarn) + m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), "https://github.com/akamai/cli-test-cmd.git", false, m.term).Return(nil).Once(). Run(func(args mock.Arguments) { mustCopyFile(t, cliJSON, cliTestCmdRepo) - input, err := ioutil.ReadFile(cliTestCmdJSON) - require.NoError(t, err) - output := strings.ReplaceAll(string(input), "${REPOSITORY_URL}", os.Getenv("REPOSITORY_URL")) - err = ioutil.WriteFile(cliTestCmdJSON, []byte(output), 0755) - require.NoError(t, err) }) - m.term.On("Spinner").Return(m.term).Once() + m.term.On("OK").Return().Once() m.term.On("Spinner").Return(m.term).Once() m.term.On("Start", "Installing...", []interface{}(nil)).Return().Once() m.langManager.On("Install", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), - packages.LanguageRequirements{Go: "1.14.0"}, []string{"app-1-cmd-1"}, []string{""}).Return(fmt.Errorf("oops")).Once() - m.term.On("Spinner").Return(m.term).Once() - m.term.On("Stop", terminal.SpinnerStatusWarn).Return().Once() - m.term.On("Writeln", []interface{}{color.CyanString("oops")}).Return(0, nil).Once() - m.term.On("IsTTY").Return(true).Once() - m.term.On("Confirm", "Binary command(s) found, would you like to download and install it?", true).Return(true, nil).Once() - m.term.On("Spinner").Return(m.term).Once() - m.term.On("Start", "Downloading binary...", []interface{}(nil)).Return().Once() + packages.LanguageRequirements{Go: "1.14.0"}, []string{"app-1-cmd-1"}, []string{""}).Return(nil).Once() m.term.On("Spinner").Return(m.term).Once() - m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + m.term.On("OK").Return().Once() // list all packages m.term.On("Printf", mock.AnythingOfType("string"), mock.Anything).Return() m.term.On("Writeln", mock.Anything).Return(0, nil) }, binaryResponseStatus: http.StatusNotFound, - withError: "Unable to install selected package", teardown: func(t *testing.T) { require.NoError(t, os.RemoveAll(cliTestCmdRepo)) }, }, "error on install from source, binary does not exist": { args: []string{"test-cmd"}, - init: func(t *testing.T, m *mocked) { + init: func(t *testing.T, m *mocked, h *httptest.Server) { m.term.On("Spinner").Return(m.term).Once() - m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + m.term.On("Start", "Attempting to fetch package configuration from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + + h = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + configJSON, err := os.ReadFile(cliNoBinaryJSON) + output := strings.ReplaceAll(string(configJSON), "${REPOSITORY_URL}", "invalid url") + require.NoError(t, err) + _, err = w.Write([]byte(output)) + require.NoError(t, err) + + })) + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" + + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Installing Binaries...", []interface{}(nil)).Return().Once() + m.term.On("Stop", terminal.SpinnerStatusWarn) + m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-test-cmd"), "https://github.com/akamai/cli-test-cmd.git", false, m.term).Return(nil).Once(). Run(func(args mock.Arguments) { @@ -359,6 +428,10 @@ func TestCmdInstall(t *testing.T) { err = ioutil.WriteFile(cliTestCmdJSON, []byte(output), 0755) require.NoError(t, err) }) + m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-test-cmd.git"}).Return().Once() + m.term.On("OK").Return().Once() + m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + m.term.On("Spinner").Return(m.term).Once() m.term.On("OK").Return().Once() m.term.On("Spinner").Return(m.term).Once() @@ -368,9 +441,7 @@ func TestCmdInstall(t *testing.T) { packages.LanguageRequirements{Go: "1.14.0"}, []string{"app-1-cmd-1"}, []string{""}).Return(fmt.Errorf("oops")).Once() m.term.On("Spinner").Return(m.term).Once() m.term.On("Stop", terminal.SpinnerStatusWarn).Return().Once() - m.term.On("Writeln", []interface{}{color.CyanString("oops")}).Return(0, nil).Once() - m.term.On("Spinner").Return(m.term).Once() - m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + m.term.On("WriteError", "oops").Return(0, nil).Once() // list all packages m.term.On("Printf", mock.AnythingOfType("string"), mock.Anything).Return() @@ -393,9 +464,12 @@ func TestCmdInstall(t *testing.T) { assert.NoError(t, err) })) defer srv.Close() + require.NoError(t, os.Setenv("REPOSITORY_URL", srv.URL)) require.NoError(t, os.Setenv("AKAMAI_CLI_HOME", filepath.Join(".", "testdata"))) m := &mocked{&terminal.Mock{}, &config.Mock{}, &git.MockRepo{}, &packages.Mock{}, nil} + h := &httptest.Server{} + command := &cli.Command{ Name: "install", Action: cmdInstall(m.gitRepo, m.langManager), @@ -405,7 +479,7 @@ func TestCmdInstall(t *testing.T) { args = append(args, "install") args = append(args, test.args...) - test.init(t, m) + test.init(t, m, h) if test.teardown != nil { defer test.teardown(t) } diff --git a/pkg/commands/command_subcommand.go b/pkg/commands/command_subcommand.go index cadca1b..f278902 100644 --- a/pkg/commands/command_subcommand.go +++ b/pkg/commands/command_subcommand.go @@ -91,7 +91,7 @@ func cmdSubcommand(git git.Repository, langManager packages.LangManager) cli.Act return err } - if _, err = installPackage(c.Context, git, langManager, commandName, false); err != nil { + if _, err = installPackage(c.Context, git, langManager, commandName); err != nil { return err } } diff --git a/pkg/commands/command_update.go b/pkg/commands/command_update.go index 5c6c23c..b0e270e 100644 --- a/pkg/commands/command_update.go +++ b/pkg/commands/command_update.go @@ -18,7 +18,9 @@ import ( "context" "errors" "fmt" + "os" "path/filepath" + "reflect" "strings" "time" @@ -54,7 +56,7 @@ func cmdUpdate(gitRepo git.Repository, langManager packages.LangManager) cli.Act for _, cmd := range getCommands(c) { for _, command := range cmd.Commands { if _, ok := builtinCmds[command.Name]; !ok { - if err := updatePackage(c.Context, gitRepo, langManager, logger, command.Name, c.Bool("force")); err != nil { + if err := updatePackage(c.Context, gitRepo, langManager, logger, command.Name); err != nil { return err } } @@ -65,7 +67,7 @@ func cmdUpdate(gitRepo git.Repository, langManager packages.LangManager) cli.Act } for _, cmd := range c.Args().Slice() { - if err := updatePackage(c.Context, gitRepo, langManager, logger, cmd, c.Bool("force")); err != nil { + if err := updatePackage(c.Context, gitRepo, langManager, logger, cmd); err != nil { return err } } @@ -74,7 +76,7 @@ func cmdUpdate(gitRepo git.Repository, langManager packages.LangManager) cli.Act } } -func updatePackage(ctx context.Context, gitRepo git.Repository, langManager packages.LangManager, logger log.Logger, cmd string, forceBinary bool) error { +func updatePackage(ctx context.Context, gitRepo git.Repository, langManager packages.LangManager, logger log.Logger, cmd string) error { term := terminal.Get(ctx) exec, _, err := findExec(ctx, langManager, cmd) if err != nil { @@ -102,16 +104,88 @@ func updatePackage(ctx context.Context, gitRepo git.Repository, langManager pack err = gitRepo.Open(repoDir) if err != nil { + logger.Debug("Unable to open repo") - term.Spinner().Fail() - return cli.Exit(color.RedString("unable to update, there an issue with the package repo: %s", err.Error()), 1) + + cmdPackage, err := readPackage(repoDir) + if err != nil { + return cli.Exit(color.RedString("unable to update, there was an issue with the package repo: %s", err.Error()), 1) + } + + packageVersions := map[string]string{} + for _, command := range cmdPackage.Commands { + packageVersions[command.Name] = command.Version + } + + repo := filepath.Base(repoDir) + url := fmt.Sprintf(githubRawURLTemplate, repo) + + remotePackage, err := readPackageFromGithub(url, repoDir) + if err != nil { + return cli.Exit(color.RedString("unable to update, there was an issue with fetching latest configuration file: %s", err.Error()), 1) + } + + remoteVersions := map[string]string{} + for _, command := range remotePackage.Commands { + remoteVersions[command.Name] = command.Version + } + + if reflect.DeepEqual(packageVersions, remoteVersions) { + term.Spinner().WarnOK() + debugMessage := fmt.Sprintf("command \"%s\" already up-to-date", cmd) + logger.Warn(debugMessage) + if _, err := term.Writeln(color.CyanString(debugMessage)); err != nil { + return err + } + return nil + } + + tempDir := filepath.Dir(repoDir) + "/.tmp_" + filepath.Base(repoDir) + logger.Debugf("Moving package to temporary dir: %s", tempDir) + if err = os.Rename(repoDir, tempDir); err != nil { + return cli.Exit(color.RedString("unable to update, there was an issue with the package repo: %s", err.Error()), 1) + } + + _, err = installPackage(ctx, gitRepo, langManager, tools.Githubize(cmd)) + if err != nil { + term.Spinner().Fail() + if err := os.Rename(tempDir, repoDir); err != nil { + return cli.Exit(color.RedString("unable to update, there was an issue with the package repo: %s", err.Error()), 1) + } + return cli.Exit(color.RedString("unable to update: %s", err.Error()), 1) + } + + if err := os.RemoveAll(tempDir); err != nil { + return cli.Exit(color.RedString("unable to update, there was an issue with the package repo: %s", err.Error()), 1) + } + + logger.Debug("Repo updated successfully") + term.Spinner().OK() + return nil + + } + + err = updateRepo(ctx, gitRepo, logger, term, cmd) + if err != nil { + return err } + if ok, _ := installPackageDependencies(ctx, langManager, repoDir, logger); !ok { + logger.Trace("Error updating dependencies") + return cli.Exit("Unable to update command", 1) + } + logger.Debug("Repo updated successfully") + term.Spinner().OK() + + return nil +} + +func updateRepo(ctx context.Context, gitRepo git.Repository, logger log.Logger, term terminal.Terminal, cmd string) error { w, err := gitRepo.Worktree() if err != nil { logger.Debug("Unable to open repo") term.Spinner().Fail() - return cli.Exit(color.RedString("unable to update, there an issue with the package repo: %s", err.Error()), 1) + return cli.Exit(color.RedString("unable to update, there was an issue with the package repo: %s", err.Error()), 1) } if err := gitRepo.Reset(&gogit.ResetOptions{Mode: gogit.HardReset}); err != nil { @@ -167,14 +241,5 @@ func updatePackage(ctx context.Context, gitRepo git.Repository, langManager pack return err } } - - logger.Debug("Repo updated successfully") - term.Spinner().OK() - - if ok, _ := installPackageDependencies(ctx, langManager, repoDir, forceBinary, logger); !ok { - logger.Trace("Error updating dependencies") - return cli.Exit("Unable to update command", 1) - } - return nil } diff --git a/pkg/commands/command_update_test.go b/pkg/commands/command_update_test.go index cf38e55..d3d7c84 100644 --- a/pkg/commands/command_update_test.go +++ b/pkg/commands/command_update_test.go @@ -6,6 +6,7 @@ import ( "net/http/httptest" "os" "path/filepath" + "strings" "testing" "github.com/akamai/cli/pkg/config" @@ -26,7 +27,7 @@ import ( func TestCmdUpdate(t *testing.T) { cliEchoRepo := filepath.Join("testdata", ".akamai-cli", "src", "cli-echo") cliEchoBin := filepath.Join("testdata", ".akamai-cli", "src", "cli-echo", "bin", "akamai-echo") - + tempTestDir := filepath.Join(".", "testdata", "temp") tests := map[string]struct { args []string init func(*testing.T, *mocked) @@ -177,6 +178,7 @@ func TestCmdUpdate(t *testing.T) { m.langManager.On("Install", cliEchoRepo, packages.LanguageRequirements{Go: "1.14.0"}, []string{"echo"}, []string{""}).Return(fmt.Errorf("oops")).Once() + m.term.On("WriteError", "oops") m.term.On("OK").Return().Once() }, @@ -279,22 +281,133 @@ func TestCmdUpdate(t *testing.T) { m.term.On("Spinner").Return(m.term).Once() m.term.On("Fail").Return().Once() }, - withError: "unable to update, there an issue with the package repo: oops", + withError: "unable to update, there was an issue with the package repo: oops", + }, + "error opening repository, up to date with remote": { + args: []string{"echo"}, + init: func(t *testing.T, m *mocked) { + m.langManager.On("FindExec", packages.LanguageRequirements{Go: "1.14.0"}, cliEchoBin).Return([]string{cliEchoBin}, nil).Once() + + h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + configJSON, err := os.ReadFile(filepath.Join(cliEchoRepo, "cli.json")) + require.NoError(t, err) + _, err = w.Write(configJSON) + require.NoError(t, err) + })) + + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", `Attempting to update "%s" command...`, []interface{}{"echo"}).Return().Once() + + m.gitRepo.On("Open", cliEchoRepo).Return(fmt.Errorf("oops")).Once() + + m.term.On("Writeln", []interface{}{color.CyanString("command \"echo\" already up-to-date")}).Return(0, nil).Once() + m.langManager.On("FindExec", packages.LanguageRequirements{Go: "1.14.0"}, cliEchoBin).Return([]string{cliEchoBin}, nil).Once() + m.term.On("Spinner").Return(m.term).Once() + m.term.On("WarnOK").Return().Once() + }, + }, + "error opening repository, update from remote, success": { + args: []string{"echo"}, + init: func(t *testing.T, m *mocked) { + + mustCopyDirectory(t, cliEchoRepo, tempTestDir) + + m.langManager.On("FindExec", packages.LanguageRequirements{Go: "1.14.0"}, cliEchoBin).Return([]string{cliEchoBin}, nil).Once() + configJSON, err := os.ReadFile(filepath.Join(cliEchoRepo, "cli.json")) + require.NoError(t, err) + h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + output := strings.ReplaceAll(string(configJSON), "1.0.0", "9.9.9") + _, err = w.Write([]byte(output)) + require.NoError(t, err) + })) + + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", `Attempting to update "%s" command...`, []interface{}{"echo"}).Return().Once() + + m.gitRepo.On("Open", cliEchoRepo).Return(fmt.Errorf("oops")).Once() + + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", `Attempting to fetch package configuration from %s...`, []interface{}{"https://github.com/akamai/cli-echo.git"}).Return().Once() + m.term.On("OK").Return().Once() + + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-echo.git"}).Return().Once() + m.term.On("OK").Return().Once() + m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + + m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-echo"), + "https://github.com/akamai/cli-echo.git", false, m.term).Return(nil).Once(). + Run(func(args mock.Arguments) { + mustCopyFile(t, filepath.Join(tempTestDir, "cli.json"), cliEchoRepo) + }) + m.term.On("OK").Return().Once() + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Installing...", []interface{}(nil)).Return().Once() + m.langManager.On("Install", filepath.Join("testdata", ".akamai-cli", "src", "cli-echo"), + packages.LanguageRequirements{Go: "1.14.0"}, []string{"echo"}, []string{""}).Return(nil).Once() + m.term.On("Spinner").Return(m.term).Once() + m.term.On("OK").Return().Once() + + }, + teardown: func(t *testing.T) { + require.NoError(t, os.RemoveAll(cliEchoRepo)) + require.NoError(t, os.Rename(tempTestDir, cliEchoRepo)) + + }, }, - "error opening repository": { + "error opening repository, update from remote, fail": { args: []string{"echo"}, init: func(t *testing.T, m *mocked) { + + mustCopyDirectory(t, cliEchoRepo, tempTestDir) + m.langManager.On("FindExec", packages.LanguageRequirements{Go: "1.14.0"}, cliEchoBin).Return([]string{cliEchoBin}, nil).Once() + configJSON, err := os.ReadFile(filepath.Join(cliEchoRepo, "cli.json")) + require.NoError(t, err) + h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + output := strings.ReplaceAll(string(configJSON), "1.0.0", "9.9.9") + _, err = w.Write([]byte(output)) + require.NoError(t, err) + })) + githubRawURLTemplate = h.URL + "/akamai/%s/master/cli.json" m.term.On("Spinner").Return(m.term).Once() m.term.On("Start", `Attempting to update "%s" command...`, []interface{}{"echo"}).Return().Once() m.gitRepo.On("Open", cliEchoRepo).Return(fmt.Errorf("oops")).Once() + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", `Attempting to fetch package configuration from %s...`, []interface{}{"https://github.com/akamai/cli-echo.git"}).Return().Once() + m.term.On("OK").Return().Once() + + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", "Attempting to fetch command from %s...", []interface{}{"https://github.com/akamai/cli-echo.git"}).Return().Once() + m.term.On("OK").Return().Once() + m.term.On("Stop", terminal.SpinnerStatusFail).Return().Once() + + m.gitRepo.On("Clone", filepath.Join("testdata", ".akamai-cli", "src", "cli-echo"), + "https://github.com/akamai/cli-echo.git", false, m.term).Return(nil).Once(). + Run(func(args mock.Arguments) { + mustCopyFile(t, filepath.Join(tempTestDir, "cli.json"), cliEchoRepo) + }) + m.term.On("OK").Return().Once() + m.term.On("Spinner").Return(m.term).Once() + + m.langManager.On("Install", filepath.Join("testdata", ".akamai-cli", "src", "cli-echo"), + packages.LanguageRequirements{Go: "1.14.0"}, []string{"echo"}, []string{""}).Return(fmt.Errorf("oops")).Once() + m.term.On("Start", "Installing...", []interface{}(nil)).Return().Once() m.term.On("Spinner").Return(m.term).Once() m.term.On("Fail").Return().Once() + m.term.On("WriteError", "oops") + + }, + teardown: func(t *testing.T) { + require.NoError(t, os.RemoveAll(cliEchoRepo)) + require.NoError(t, os.Rename(tempTestDir, cliEchoRepo)) }, - withError: "unable to update, there an issue with the package repo: oops", + withError: "unable to update: Unable to install selected package", }, "error finding executable": { args: []string{"not-found"}, @@ -339,7 +452,9 @@ func TestCmdUpdate(t *testing.T) { assert.Contains(t, err.Error(), test.withError) return } + require.NoError(t, err) }) } + } diff --git a/pkg/commands/helpers_test.go b/pkg/commands/helpers_test.go index 683647d..ab124ce 100644 --- a/pkg/commands/helpers_test.go +++ b/pkg/commands/helpers_test.go @@ -136,3 +136,55 @@ func copyFile(src, dst string) error { func mustCopyFile(t *testing.T, src, dst string) { require.NoError(t, copyFile(src, dst)) } + +func mustCopyDirectory(t *testing.T, src, dst string) { + require.NoError(t, copyDirectory(src, dst)) +} + +func copyDirectory(src, dst string) error { + entries, err := os.ReadDir(src) + if err != nil { + return err + } + for _, entry := range entries { + sourcePath := filepath.Join(src, entry.Name()) + destPath := filepath.Join(dst, entry.Name()) + + fileInfo, err := os.Stat(sourcePath) + if err != nil { + return err + } + + if fileInfo.Mode().IsDir() { + perm := fileInfo.Mode().Perm() + if err := createIfNotExists(destPath, perm); err != nil { + return err + } + if err := copyDirectory(sourcePath, destPath); err != nil { + return err + } + } else { + if err := copyFile(sourcePath, dst); err != nil { + return err + } + } + + } + return nil +} + +func exists(path string) bool { + if _, err := os.Stat(path); os.IsNotExist(err) { + return false + } + return true +} + +func createIfNotExists(dir string, perm os.FileMode) error { + if exists(dir) { + return nil + } + + err := os.MkdirAll(dir, perm) + return err +} diff --git a/pkg/commands/subcommands.go b/pkg/commands/subcommands.go index 5c269fb..08bdac8 100644 --- a/pkg/commands/subcommands.go +++ b/pkg/commands/subcommands.go @@ -39,6 +39,7 @@ type subcommands struct { Requirements packages.LanguageRequirements `json:"requirements"` Action cli.ActionFunc `json:"-"` Pkg string `json:"pkg"` + raw []byte } func readPackage(dir string) (subcommands, error) { @@ -69,6 +70,39 @@ func readPackage(dir string) (subcommands, error) { return packageData, nil } +func readPackageFromGithub(url, dir string) (subcommands, error) { + response, err := http.Get(url) + if err != nil { + return subcommands{}, err + } + if response.StatusCode == http.StatusOK { + cliJSON, err := io.ReadAll(response.Body) + if err != nil { + return subcommands{}, err + } + + var packageData subcommands + + err = json.Unmarshal(cliJSON, &packageData) + if err != nil { + return subcommands{}, err + } + + packageData.raw = cliJSON + + for key := range packageData.Commands { + packageData.Commands[key].Name = strings.ToLower(packageData.Commands[key].Name) + } + + packageData.Pkg = filepath.Base(strings.Replace(dir, "cli-", "", 1)) + + return packageData, nil + + } + + return subcommands{}, fmt.Errorf("Invalid response status while fetching cli.json: %d", response.StatusCode) +} + func getPackagePaths() []string { akamaiCliPath, err := tools.GetAkamaiCliSrcPath() if err == nil && akamaiCliPath != "" { @@ -81,6 +115,16 @@ func getPackagePaths() []string { return []string{} } +func isBinary(cmdPackage subcommands) bool { + for _, cmd := range cmdPackage.Commands { + if len(cmd.Bin) == 0 { + return false + } + } + return true + +} + func findPackageDir(dir string) string { if stat, err := os.Stat(dir); err == nil && stat != nil && !stat.IsDir() { dir = filepath.Dir(dir) @@ -88,7 +132,7 @@ func findPackageDir(dir string) string { if _, err := os.Stat(filepath.Join(dir, "cli.json")); err != nil { if os.IsNotExist(err) { - if filepath.Dir(dir) == "" || filepath.Dir(dir) == "." { + if filepath.Dir(dir) == "" || filepath.Dir(dir) == "." || filepath.Dir(dir) == "/" { return "" } diff --git a/pkg/commands/testdata/.akamai-cli/src/cli-echo-python/cli.json b/pkg/commands/testdata/.akamai-cli/src/cli-echo-python/cli.json index c1e4d05..825d15b 100644 --- a/pkg/commands/testdata/.akamai-cli/src/cli-echo-python/cli.json +++ b/pkg/commands/testdata/.akamai-cli/src/cli-echo-python/cli.json @@ -6,7 +6,8 @@ { "name": "echo-python", "aliases": ["e"], - "description": "echo command" + "description": "echo command", + "version": "1.0.0" } ] } diff --git a/pkg/commands/testdata/.akamai-cli/src/cli-echo/cli.json b/pkg/commands/testdata/.akamai-cli/src/cli-echo/cli.json index 87687de..4ab094d 100644 --- a/pkg/commands/testdata/.akamai-cli/src/cli-echo/cli.json +++ b/pkg/commands/testdata/.akamai-cli/src/cli-echo/cli.json @@ -6,7 +6,8 @@ { "name": "echo", "aliases": ["e"], - "description": "echo command" + "description": "echo command", + "version": "1.0.0" } ] } diff --git a/pkg/commands/testdata/.akamai-cli/src/cli-installed/cli.json b/pkg/commands/testdata/.akamai-cli/src/cli-installed/cli.json index 284a70a..121fe78 100644 --- a/pkg/commands/testdata/.akamai-cli/src/cli-installed/cli.json +++ b/pkg/commands/testdata/.akamai-cli/src/cli-installed/cli.json @@ -6,7 +6,8 @@ { "name": "installed", "aliases": ["ac2"], - "description": "Test command" + "description": "Test command", + "version": "1.0.0" } ] } From eed6d0173924a4006c9989dc78b1410b15929b73 Mon Sep 17 00:00:00 2001 From: Shristi Singh Date: Mon, 27 May 2024 09:22:03 +0000 Subject: [PATCH 5/9] DXE-3012 CLI Uninstall command clean-up --- CHANGELOG.md | 1 + pkg/commands/command_uninstall.go | 25 +++++++++++++++++++++++++ pkg/commands/command_uninstall_test.go | 17 ++++++++++++++--- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db436e1..21dff4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * --force flag has been deprecated for both install and update * Migrated to go 1.21 * Updated various dependencies +* Enabled uninstall command when binaries were not found ### Fixes diff --git a/pkg/commands/command_uninstall.go b/pkg/commands/command_uninstall.go index abf4dd9..f56e26f 100644 --- a/pkg/commands/command_uninstall.go +++ b/pkg/commands/command_uninstall.go @@ -16,9 +16,11 @@ package commands import ( "context" + "errors" "fmt" "os" "path/filepath" + "strings" "time" "github.com/akamai/cli/pkg/log" @@ -26,6 +28,7 @@ import ( "github.com/akamai/cli/pkg/terminal" "github.com/akamai/cli/pkg/tools" "github.com/fatih/color" + "github.com/mitchellh/go-homedir" "github.com/urfave/cli/v2" ) @@ -56,8 +59,30 @@ func cmdUninstall(langManager packages.LangManager) cli.ActionFunc { func uninstallPackage(ctx context.Context, langManager packages.LangManager, cmd string, logger log.Logger) error { term := terminal.Get(ctx) + home, err := homedir.Dir() + if err != nil { + return fmt.Errorf("no home directory detected: %s", err) + } + home += string(filepath.Separator) exec, _, err := findExec(ctx, langManager, cmd) if err != nil { + if !errors.Is(err, packages.ErrNoExeFound) { + return fmt.Errorf("command \"%s\" not found. Try \"%s help\" : %s", cmd, tools.Self(), err) + } + // err = ErrNoExeFound - there is a directory but without any executables + paths := filepath.SplitList(getPackageBinPaths()) + for i, path := range paths { + + // trim home directory part of a path to exclude cases where command name could be a part of it + path = strings.TrimPrefix(path, home) + // if trimmed path (akamai-cli defined) contains name of command to uninstall, delete directory + if strings.Contains(path, cmd) { + if err = os.RemoveAll(paths[i]); err != nil { + return fmt.Errorf("could not remove directory %s: %s", paths[i], err) + } + return nil + } + } return fmt.Errorf("command \"%s\" not found. Try \"%s help\"", cmd, tools.Self()) } diff --git a/pkg/commands/command_uninstall_test.go b/pkg/commands/command_uninstall_test.go index 155f87d..f70800d 100644 --- a/pkg/commands/command_uninstall_test.go +++ b/pkg/commands/command_uninstall_test.go @@ -81,11 +81,22 @@ func TestCmdUninstall(t *testing.T) { }, withError: "unable to uninstall, was it installed using " + color.CyanString("\"akamai install\"") + "?", }, - "executable not found": { - args: []string{"invalid"}, + "uninstall command when executable not found": { + args: []string{"echo-uninstall"}, init: func(t *testing.T, m *mocked) { + mustCopyFile(t, cliEchoJSON, cliEchoUninstallRepo) + mustCopyFile(t, cliEchoBin, cliEchoUninstallBinDir) + m.langManager.On("FindExec", packages.LanguageRequirements{Go: "1.14.0"}, "echo-uninstall").Return([]string{}, packages.ErrNoExeFound).Once() + m.langManager.On("GetPackageBinPaths").Return("/path/to/echo-uninstall").Once() + + m.term.On("Spinner").Return(m.term).Once() + m.term.On("Start", `Attempting to uninstall "echo-uninstall" command...`, []interface{}(nil)).Return().Once() + m.term.On("Spinner").Return(m.term).Once() + m.term.On("OK").Return().Once() + + m.term.On("RemoveAll", "/path/to/echo-uninstall").Return(nil).Once() + }, - withError: fmt.Sprintf(`command "invalid" not found. Try "%s help"`, tools.Self()), }, } From f3d72d848a2336ff79bd3792b52e4ce04bc3eced Mon Sep 17 00:00:00 2001 From: Filip Antkowiak Date: Wed, 5 Jun 2024 10:38:14 +0000 Subject: [PATCH 6/9] DXE-3781 fix missing error message --- pkg/commands/command_install.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/commands/command_install.go b/pkg/commands/command_install.go index 788c572..83fba5e 100644 --- a/pkg/commands/command_install.go +++ b/pkg/commands/command_install.go @@ -154,7 +154,10 @@ func installPackage(ctx context.Context, gitRepo git.Repository, langManager pac if _, err := term.Writeln(err.Error()); err != nil { term.WriteError(err.Error()) } - return nil, cli.Exit("Unable to install selected package", 1) + if strings.Contains(err.Error(), "404") { + return nil, cli.Exit(color.RedString(tools.CapitalizeFirstWord(git.ErrPackageNotAvailable.Error())), 1) + } + return nil, cli.Exit(color.RedString("Unable to install selected package"), 1) } spin.OK() From b1c8c44f02fb89767e148235631af2f4d2648699 Mon Sep 17 00:00:00 2001 From: Shristi Singh Date: Fri, 28 Jun 2024 05:38:33 +0000 Subject: [PATCH 7/9] DXE-3553 Remove versions from CLI package-list.json Merge in DEVEXP/cli from feature/DXE-3553 to develop --- CHANGELOG.md | 1 + pkg/commands/command_search.go | 71 +++++++++++++- pkg/commands/command_search_test.go | 100 ++++++++++++++++++-- pkg/commands/package_list/package-list.json | 79 +++++----------- 4 files changed, 188 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21dff4a..02cf178 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## X.X.X (X X, X) ### Enhancements +* Removed commands and packages versions from `package-list.json` * Changed package installation order * Cli will first check if new binaries are available, if the package has no binaries or no valid binaries can be found, it will build the package locally. * --force flag has been deprecated for both install and update diff --git a/pkg/commands/command_search.go b/pkg/commands/command_search.go index dbdd56a..c89fece 100644 --- a/pkg/commands/command_search.go +++ b/pkg/commands/command_search.go @@ -16,7 +16,12 @@ package commands import ( "context" + "encoding/json" "fmt" + "io" + "net/http" + "net/url" + "path" "sort" "strings" "time" @@ -28,6 +33,10 @@ import ( "github.com/urfave/cli/v2" ) +var ( + githubURLTemplate = "https://raw.githubusercontent.com/akamai/%s/master/cli.json" +) + func cmdSearch(c *cli.Context) (e error) { pr := newPackageReader(embeddedPackages) return cmdSearchWithPackageReader(c, pr) @@ -148,7 +157,14 @@ func searchPackages(ctx context.Context, keywords []string, packageList *package } term.Printf(bold.Sprintf(" Command:")+" %s %s\n", cmd.Name, aliases) - term.Printf(bold.Sprintf(" Version:")+" %s\n", cmd.Version) + + url := results[hits][pkgName].URL + latestVersion, err := getLatestVersion(url) + if err != nil { + return cli.Exit(color.RedString(err.Error()), 1) + } + term.Printf(bold.Sprintf(" Latest Version:")+" %s\n", latestVersion) + term.Printf(bold.Sprintf(" Description:")+" %s\n\n", cmd.Description) } } @@ -161,3 +177,56 @@ func searchPackages(ctx context.Context, keywords []string, packageList *package return nil } + +func getLatestVersion(s string) (string, error) { + + u, err := url.Parse(s) + if err != nil { + return "", fmt.Errorf("error parsing URL: %s", err.Error()) + } + + // extract the last string of the package URL + lastSegment := path.Base(u.Path) + + repoURL := fmt.Sprintf(githubURLTemplate, lastSegment) + resp, err := http.Get(repoURL) + if err != nil { + return "", fmt.Errorf("error fetching the URL: %s", err.Error()) + } + defer func() { + if err := resp.Body.Close(); err != nil { + fmt.Println("error closing the response body:", err) + } + }() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("error: status code %d", resp.StatusCode) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("error reading the response body: %w", err) + } + + var cli CLI + if err := json.Unmarshal(body, &cli); err != nil { + return "", fmt.Errorf("error parsing the JSON: %w", err) + } + + if len(cli.CommandList) > 0 { + return cli.CommandList[0].Version, nil + } + return "", fmt.Errorf("no latest version found") +} + +// CLI struct represents an individual command object in package-list.json +type CLI struct { + CommandList []CommandObject `json:"commands"` +} + +// CommandObject contains details for particular command +type CommandObject struct { + Name string `json:"name"` + Version string `json:"version"` + Description string `json:"description"` +} diff --git a/pkg/commands/command_search_test.go b/pkg/commands/command_search_test.go index c1a6220..f8a020d 100644 --- a/pkg/commands/command_search_test.go +++ b/pkg/commands/command_search_test.go @@ -2,6 +2,8 @@ package commands import ( "encoding/json" + "net/http" + "net/http/httptest" "os" "testing" @@ -31,8 +33,11 @@ func TestCmdSearch(t *testing.T) { Return().Once() m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"sample", ""}). Return().Once() - m.On("Printf", bold.Sprintf(" Version:")+" %s\n", []interface{}{"2.0.0"}). - Return().Once() + + h := mockedServer("sample", "2.0.0", t) + githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" + + m.On("Printf", bold.Sprintf(" Latest Version:")+" %s\n", []interface{}{"2.0.0"}).Return().Once() m.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for single match"}). Return().Once() @@ -54,7 +59,11 @@ func TestCmdSearch(t *testing.T) { Return().Once() m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"cli-2", "(aliases: abc, abc2)"}). Return().Once() - m.On("Printf", bold.Sprintf(" Version:")+" %s\n", []interface{}{"1.0.0"}). + + h := mockedServer("cli-2", "1.0.0", t) + githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" + + m.On("Printf", bold.Sprintf(" Latest Version:")+" %s\n", []interface{}{"1.0.0"}). Return().Once() m.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for match on name"}). Return().Once() @@ -63,7 +72,10 @@ func TestCmdSearch(t *testing.T) { Return().Once() m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"ClI-1", ""}). Return().Once() - m.On("Printf", bold.Sprintf(" Version:")+" %s\n", []interface{}{"1.0.0"}). + h = mockedServer("CLI-1", "1.0.0", t) + githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" + + m.On("Printf", bold.Sprintf(" Latest Version:")+" %s\n", []interface{}{"1.0.0"}). Return().Once() m.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for match on title"}). Return().Once() @@ -72,7 +84,10 @@ func TestCmdSearch(t *testing.T) { Return().Once() m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"cli", ""}). Return().Once() - m.On("Printf", bold.Sprintf(" Version:")+" %s\n", []interface{}{"1.0.0"}). + h = mockedServer("cli", "1.0.0", t) + githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" + + m.On("Printf", bold.Sprintf(" Latest Version:")+" %s\n", []interface{}{"1.0.0"}). Return().Once() m.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for match on command name"}). Return().Once() @@ -81,7 +96,10 @@ func TestCmdSearch(t *testing.T) { Return().Once() m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"abc-3", ""}). Return().Once() - m.On("Printf", bold.Sprintf(" Version:")+" %s\n", []interface{}{"1.0.0"}). + + h = mockedServer("abc-3", "1.0.0", t) + githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" + m.On("Printf", bold.Sprintf(" Latest Version:")+" %s\n", []interface{}{"1.0.0"}). Return().Once() m.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"CLI - test for match on description"}). Return().Once() @@ -103,6 +121,49 @@ func TestCmdSearch(t *testing.T) { init: func(m *terminal.Mock) {}, withError: "You must specify one or more keywords", }, + "search and find single package - 404": { + args: []string{"sample"}, + init: func(m *terminal.Mock) { + bold := color.New(color.FgWhite, color.Bold) + m.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{1}) + + m.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"sample", color.BlueString("SAMPLE")}). + Return().Once() + m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"sample", ""}). + Return().Once() + + h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "Not Found", http.StatusNotFound) + })) + defer h.Close() + }, + packages: packagesForTest, + withError: "error: status code 400", + }, + "search and find single package - when no latest version found": { + args: []string{"sample"}, + init: func(m *terminal.Mock) { + bold := color.New(color.FgWhite, color.Bold) + m.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{1}) + + m.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"sample", color.BlueString("SAMPLE")}). + Return().Once() + m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"sample", ""}). + Return().Once() + + h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + mockResponse := CLI{ + CommandList: []CommandObject{}, + } + respBody, _ := json.Marshal(mockResponse) + w.WriteHeader(http.StatusOK) + var _, _ = w.Write(respBody) + })) + githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" + }, + withError: "no latest version found", + packages: packagesForTest, + }, } for name, test := range tests { @@ -255,3 +316,30 @@ var packagesForTest = &packageList{ }, }, } + +func mockedServer(name, version string, t *testing.T) *httptest.Server { + h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + mockResponse := CLI{ + CommandList: []CommandObject{ + { + Name: name, + Version: version, + }, + }, + } + respBody, err := json.Marshal(mockResponse) + if err != nil { + t.Errorf("Error marshalling the response: %v", err) + t.Fail() + return + } + w.WriteHeader(http.StatusOK) + _, err = w.Write(respBody) + if err != nil { + t.Errorf("Error writing the response: %v", err) + t.Fail() + } + + })) + return h +} diff --git a/pkg/commands/package_list/package-list.json b/pkg/commands/package_list/package-list.json index 579f27b..b192561 100644 --- a/pkg/commands/package_list/package-list.json +++ b/pkg/commands/package_list/package-list.json @@ -4,36 +4,31 @@ { "title": "Adaptive Acceleration", "name": "adaptive-acceleration", - "version": "0.1", "url": "https://github.com/akamai/cli-adaptive-acceleration", "issues": "https://github.com/akamai/cli-adaptive-acceleration/issues", - "commands": [{"name":"adaptive-acceleration","aliases":["a2"],"version":"0.1","description":"Reset A2 Push and Preconnect policy"}], + "commands": [{"name":"adaptive-acceleration","aliases":["a2"],"description":"Reset A2 Push and Preconnect policy"}], "requirements": {"python":"3.0.0"} }, { "title": "API Gateway", "name": "akamai/api-gateway", - "version": "0.1.0", "url": "https://github.com/akamai/cli-api-gateway", "issues": "https://github.com/akamai/cli-api-gateway/issues", "commands": [ { "name": "api-gateway", - "version": "0.1.0", "description": "Manage API definitions and endpoints", "auto-complete": true, "bin": "https://github.com/akamai/cli-api-gateway/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}" }, { "name": "api-keys", - "version": "0.1.0", "description": "Manage API keys", "auto-complete": true, "bin": "https://github.com/akamai/cli-api-gateway/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}" }, { "name": "api-security", - "version": "0.1.0", "description": "Manage API protections", "auto-complete": true, "bin": "https://github.com/akamai/cli-api-gateway/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}" @@ -46,25 +41,22 @@ { "title": "Application Security", "name": "appsec", - "version": "2.8.0", "url": "https://github.com/akamai/cli-appsec", "issues": "https://github.com/akamai/cli-appsec/issues", - "commands": [{"name":"appsec","version":"2.8.0","description":"Akamai Security tools for protecting websites."}], + "commands": [{"name":"appsec","description":"Akamai Security tools for protecting websites."}], "requirements": {"node":"10.0.0"} }, { "title": "Client Access Control (CAC)", "name": "cac", - "version": "v1.0.8", "url": "https://github.com/akamai/cli-cac", "issues": "https://github.com/akamai/cli-cac/issues", - "commands": [{"name":"cac","aliases":["cac"],"version":"v1.0.8","description":"An Akamai CLI package for Client Access Control"}], + "commands": [{"name":"cac","aliases":["cac"],"description":"An Akamai CLI package for Client Access Control"}], "requirements": {"python":"3.0.0"} }, { "title": "Certificate Provisioning Service (CPS)", "name": "cps", - "version": "v2.0.0", "url": "https://github.com/akamai/cli-cps", "issues": "https://github.com/akamai/cli-cps/issues", "commands": [ @@ -73,7 +65,6 @@ "aliases": [ "certs" ], - "version": "2.0.0", "description": "Access Certificate Provisioning System (CPS) Information" } ], @@ -84,23 +75,20 @@ { "title": "Cloudlets", "name": "cloudlets", - "version": "v1.1.1", "url": "https://github.com/akamai/cli-cloudlets", "issues": "https://github.com/akamai/cli-cloudlets/issues", - "commands": [{"name":"cloudlets","aliases":["cloudlets"],"version":"1.1.1","description":"Manage Akamai Cloudlets"}], + "commands": [{"name":"cloudlets","aliases":["cloudlets"],"description":"Manage Akamai Cloudlets"}], "requirements": {"python":"3.6.0"} }, { "title": "Diagnostics", "name": "diagnostics", - "version": "v1.1.0", "url": "https://github.com/akamai/cli-diagnostics", "issues": "https://github.com/akamai/cli-diagnostics/issues", "commands": [ { "name":"diagnostics", "aliases":["diag", "edge-diagnostics"], - "version":"v1.1.0", "description":"Edge Diagnostics enables you to identify, analyze, and troubleshoot common content delivery network issues that your users may encounter.", "bin": "https://github.com/akamai/cli-diagnostics/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}", "auto-complete": true @@ -111,43 +99,38 @@ { "title": "EdgeWorkers", "name": "edgeworkers", - "version": "1.7.3", "url": "https://github.com/akamai/cli-edgeworkers", "issues": "https://github.com/akamai/cli-edgeworkers/issues", "commands": [ - {"name":"edgeworkers","aliases":["ew", "edgeworkers"],"version":"1.7.3","description":"Manage Akamai EdgeWorkers code bundles."}, - {"name":"edgekv","aliases":["ekv", "edgekv"],"version":"1.7.3","description":"Manage Akamai EdgeKV database."} + {"name":"edgeworkers","aliases":["ew", "edgeworkers"],"description":"Manage Akamai EdgeWorkers code bundles."}, + {"name":"edgekv","aliases":["ekv", "edgekv"],"description":"Manage Akamai EdgeKV database."} ], "requirements": {"node":"14.0.0"} }, { "title": "Akamai Sandbox", "name": "sandbox", - "version": "v1.7.1", "url": "https://github.com/akamai/cli-sandbox", "issues": "https://github.com/akamai/cli-sandbox/issues", - "commands": [{"name":"sandbox","version":"1.7.1","description":"Manage Akamai Sandbox environments.","bin": "https://github.com/akamai/cli-sandbox/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}-{{.Arch}}{{.BinSuffix}}"}], + "commands": [{"name":"sandbox","description":"Manage Akamai Sandbox environments.","bin": "https://github.com/akamai/cli-sandbox/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}-{{.Arch}}{{.BinSuffix}}"}], "requirements": {"node":"14.21.3"} }, { "title": "Edge DNS", "name": "dns", - "version": "0.5.0", "url": "https://github.com/akamai/cli-dns", "issues": "https://github.com/akamai/cli-dns/issues", - "commands": [{"name":"dns","version":"0.5.0","description":"Manage DNS zones with Edge DNS","bin":"https://github.com/akamai/cli-dns/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}","auto-complete":true}], + "commands": [{"name":"dns","description":"Manage DNS zones with Edge DNS","bin":"https://github.com/akamai/cli-dns/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}","auto-complete":true}], "requirements": {"go":"1.18.0"} }, { "title": "Enterprise Application Access", "name": "eaa", - "version": "0.6.3", "url": "https://github.com/akamai/cli-eaa", "issues": "https://github.com/akamai/cli-eaa/issues", "commands": [ { "name": "eaa", - "version": "0.6.3", "description": "Akamai CLI for Enterprise Application Access (EAA)" } ], @@ -158,40 +141,36 @@ { "title": "Firewall and Site Shield", "name": "firewall", - "version": "v0.2.2", "url": "https://github.com/akamai/cli-firewall", "issues": "https://github.com/akamai/cli-firewall/issues", "commands": [ - {"name":"firewall","aliases":["fw"],"version":"0.2.3","description":"Access Akamai Firewall Rules Services, Subscriptions, and CIDRs"}, - {"name":"site-shield","aliases":["ss"],"version":"0.2.3","description":"Access details of Site-Shield Maps, CIDRs and acknowledgement"} + {"name":"firewall","aliases":["fw"],"description":"Access Akamai Firewall Rules Services, Subscriptions, and CIDRs"}, + {"name":"site-shield","aliases":["ss"],"description":"Access details of Site-Shield Maps, CIDRs and acknowledgement"} ], "requirements": {"python":"3.0.0"} }, { "title": "Global Traffic Management", "name": "gtm", - "version": "0.5.0", "url": "https://github.com/akamai/cli-gtm", "issues": "https://github.com/akamai/cli-gtm/issues", - "commands": [{"name":"gtm","version":"0.5.0","description":"Manage GTM Domains","bin":"https://github.com/akamai/cli-gtm/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}","auto-complete":true}], + "commands": [{"name":"gtm","description":"Manage GTM Domains","bin":"https://github.com/akamai/cli-gtm/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}","auto-complete":true}], "requirements": {"go":"1.18.0"} }, { "title": "Image Manager", "name": "image-manager", - "version": "0.1.9", "url": "https://github.com/akamai/cli-image-manager", "issues": "https://github.com/akamai/cli-image-manager/issues", "commands": [ - {"name":"image-manager","aliases":["im"],"version":"0.1.9","description":"An Akamai CLI package for Image Manager"}, - {"name":"video-manager","aliases":["vm"],"version":"0.1.9","description":"An Akamai CLI package for Video Manager"} + {"name":"image-manager","aliases":["im"],"description":"An Akamai CLI package for Image Manager"}, + {"name":"video-manager","aliases":["vm"],"description":"An Akamai CLI package for Video Manager"} ], "requirements": {"python":"3.0.0"} }, { "title": "Jsonnet", "name": "jsonnet", - "version": "0.9.0", "url": "https://github.com/akamai/cli-jsonnet", "issues": "https://github.com/akamai/cli-jsonnet/issues", "commands": [ @@ -200,7 +179,6 @@ "aliases": [ "jsonnet" ], - "version": "0.9.0", "description": "Utilities for managing Akamai as jsonnet" } ], @@ -211,61 +189,54 @@ { "title": "NetStorage", "name": "netstorage", - "version": "1.0.1", "url": "https://github.com/akamai/cli-netstorage", "issues": "https://github.com/akamai/cli-netstorage/issues", - "commands": [{"name":"netstorage","version":"1.0.1","description":"Interface for Akamai NetStorage","bin":"https://github.com/akamai/cli-netstorage/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}-{{.Arch}}{{.BinSuffix}}"}], + "commands": [{"name":"netstorage","description":"Interface for Akamai NetStorage","bin":"https://github.com/akamai/cli-netstorage/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}-{{.Arch}}{{.BinSuffix}}"}], "requirements": {"node":"7.0.0"} }, { "title": "Onboard", "name": "onboard", - "version": "2.3.1", "url": "https://github.com/akamai/cli-onboard", "issues": "https://github.com/akamai/cli-onboard/issues", - "commands": [{"name":"onboard","aliases":["onboard"],"version":"2.3.1","description":"Onboard Akamai delivery and WAF configuration"}], + "commands": [{"name":"onboard","aliases":["onboard"],"description":"Onboard Akamai delivery and WAF configuration"}], "requirements": {"python":"3.6.0"} }, { "title": "Property Manager", "name": "property-manager", - "version": "0.7.8-RELEASE", "url": "https://github.com/akamai/cli-property-manager", "issues": "https://github.com/akamai/cli-property-manager/issues", "commands": [ - {"name":"property-manager","aliases":["pm","snippets"],"version":"0.7.8-RELEASE","description":"Property Manager CLI for DevOps"}, - {"name":"pipeline","aliases":["pl","pd","proddeploy"],"version":"0.7.8-RELEASE","description":"Akamai Pipeline for DevOps"} + {"name":"property-manager","aliases":["pm","snippets"],"description":"Property Manager CLI for DevOps"}, + {"name":"pipeline","aliases":["pl","pd","proddeploy"],"description":"Akamai Pipeline for DevOps"} ], "requirements": {"node":"8.9.1"} }, { "title": "Purge", "name": "purge", - "version": "1.1.0", "url": "https://github.com/akamai/cli-purge", "issues": "https://github.com/akamai/cli-purge/issues", - "commands": [{"name":"purge","version":"1.1.0","description":"Purge content from the Edge","bin":"https://github.com/akamai/cli-purge/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}","auto-complete":true}], + "commands": [{"name":"purge","description":"Purge content from the Edge","bin":"https://github.com/akamai/cli-purge/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}","auto-complete":true}], "requirements": {"go":"1.18.0"} }, { "title": "Terraform Client Configuration", "name": "cli terraform", - "version": "1.12.0", "url": "https://github.com/akamai/cli-terraform", "issues": "https://github.com/akamai/cli-terraform/issues", - "commands": [{"name":"terraform","version":"1.12.0","description":"Administer and Manage Akamai Terraform configurations","bin":"https://github.com/akamai/cli-terraform/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}","auto-complete":true,"ldflags": "-X 'github.com/akamai/cli-terraform/cli.Version=%s'"}], + "commands": [{"name":"terraform","description":"Administer and Manage Akamai Terraform configurations","bin":"https://github.com/akamai/cli-terraform/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}","auto-complete":true,"ldflags": "-X 'github.com/akamai/cli-terraform/cli.Version=%s'"}], "requirements": {"go":"1.21.0"} }, { "title": "Test Center", "name": "test-center", - "version": "1.0.0", "url": "https://github.com/akamai/cli-test-center", "issues": "https://github.com/akamai/cli-test-center/issues", "commands": [ { "name": "test-center", - "version": "1.0.0", "description": "Test Center is a testing tool that checks the effect of configuration changes on your web property. Use this tool as part of your testing protocol to increase your confidence in the safety and accuracy of your configuration changes.", "bin": "https://github.com/akamai/cli-test-center/releases/download/{{.Version}}/akamai-{{.Name}}-{{.Version}}-{{.OS}}{{.Arch}}{{.BinSuffix}}" } @@ -275,37 +246,33 @@ { "title": "Visitor Prioritization", "name": "visitor-prioritization", - "version": "v0.3.0", "url": "https://github.com/akamai/cli-visitor-prioritization", "issues": "https://github.com/akamai/cli-visitor-prioritization/issues", - "commands": [{"name":"visitor-prioritization","aliases":["vp"],"version":"0.3.0","description":"Access and control Visitor Prioritization cloudlet"}], + "commands": [{"name":"visitor-prioritization","aliases":["vp"],"description":"Access and control Visitor Prioritization cloudlet"}], "requirements": {"python":"3.0.0"} }, { "title": "Enterprise Threat Protector", "name": "etp", - "version": "v0.4.5", "url": "https://github.com/akamai/cli-etp", "issues": "https://github.com/akamai/cli-etp/issues", - "commands": [{"name":"etp","version":"v0.4.5", "description":"Akamai CLI for Secure Internet Access Enterprise (f.k.a. Enterprise Threat Protector)"}], + "commands": [{"name":"etp","description":"Akamai CLI for Secure Internet Access Enterprise (f.k.a. Enterprise Threat Protector)"}], "requirements": {"python":"3.6.0"} }, { "title": "Akamai MFA", "name": "mfa", - "version": "v0.1.1", "url": "https://github.com/akamai/cli-mfa", "issues": "https://github.com/akamai/cli-mfa/issues", - "commands": [{"name":"mfa", "version":"v0.1.1", "description":"Akamai CLI for Akamai MFA"}], + "commands": [{"name":"mfa","description":"Akamai CLI for Akamai MFA"}], "requirements": {"python":"3.7.0"} }, { "title": "mPulse", "name": "mpulse", - "version": "v0.2.0", "url": "https://github.com/akamai/cli-mpulse", "issues": "https://github.com/akamai/cli-mpulse/issues", - "commands": [{"name":"mpulse", "aliases": ["mp"], "version":"v0.2.0", "description":"Get mPulse reports for your applications"}], + "commands": [{"name":"mpulse", "aliases": ["mp"],"description":"Get mPulse reports for your applications"}], "requirements": {"python":"3.0.0"} } ] From 9ebdeacef10681f42c9c77137b787c5c1df98813 Mon Sep 17 00:00:00 2001 From: Shristi Singh Date: Tue, 23 Jul 2024 13:25:13 +0000 Subject: [PATCH 8/9] DXE-4010 fetching version of the package present in the user home directory Merge in DEVEXP/cli from feature/DXE-4010 to develop --- CHANGELOG.md | 1 + pkg/commands/command_list_test.go | 4 + pkg/commands/command_search.go | 62 +++++++- pkg/commands/command_search_test.go | 166 ++++++++++++++------ pkg/commands/package_list/package-list.json | 2 +- 5 files changed, 179 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02cf178..da9d736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## X.X.X (X X, X) ### Enhancements +* Support to show the `Installed Version` of commands during `search` * Removed commands and packages versions from `package-list.json` * Changed package installation order * Cli will first check if new binaries are available, if the package has no binaries or no valid binaries can be found, it will build the package locally. diff --git a/pkg/commands/command_list_test.go b/pkg/commands/command_list_test.go index dea6c38..ca3b411 100644 --- a/pkg/commands/command_list_test.go +++ b/pkg/commands/command_list_test.go @@ -75,6 +75,10 @@ func TestCmdListWithRemote(t *testing.T) { m.term.On("Writeln", []interface{}{fmt.Sprintf(" [package: %s]", color.BlueString("SAMPLE"))}).Return(0, nil).Once() m.term.On("Printf", " test for single match\n", []interface{}(nil)).Return().Once() + m.term.On("Printf", bold.Sprint(" echo-uninstall"), []interface{}(nil)).Return().Once() + m.term.On("Writeln", []interface{}{fmt.Sprintf(" [package: %s]", color.BlueString("echo"))}).Return(0, nil).Once() + m.term.On("Printf", " test for single match\n", []interface{}(nil)).Return().Once() + m.term.On("Printf", "\nInstall using \"%s\".\n", []interface{}{color.BlueString("%s install [package]", tools.Self())}).Return().Once() }, packages: packagesForTest, diff --git a/pkg/commands/command_search.go b/pkg/commands/command_search.go index c89fece..9fec11b 100644 --- a/pkg/commands/command_search.go +++ b/pkg/commands/command_search.go @@ -21,7 +21,9 @@ import ( "io" "net/http" "net/url" + "os" "path" + "path/filepath" "sort" "strings" "time" @@ -143,28 +145,39 @@ func searchPackages(ctx context.Context, keywords []string, packageList *package term.Printf(color.YellowString("Results Found:")+" %d\n\n", len(resultPkgs)) + return printResult(resultHits, resultPkgs, results, term, bold) +} + +func printResult(resultHits []int, resultPkgs []string, results map[int]map[string]packageListItem, term terminal.Terminal, bold *color.Color) error { + var installedVersion, availableVersion string for _, hits := range resultHits { for _, pkgName := range resultPkgs { if _, ok := results[hits][pkgName]; ok { pkg := results[hits][pkgName] term.Printf(color.GreenString("Package: ")+"%s [%s]\n", pkg.Title, color.BlueString(pkg.Name)) - for _, cmd := range results[hits][pkgName].Commands { + for _, cmd := range pkg.Commands { var aliases string if len(cmd.Aliases) == 1 { aliases = fmt.Sprintf("(alias: %s)", cmd.Aliases[0]) } else if len(cmd.Aliases) > 1 { aliases = fmt.Sprintf("(aliases: %s)", strings.Join(cmd.Aliases, ", ")) } - term.Printf(bold.Sprintf(" Command:")+" %s %s\n", cmd.Name, aliases) - url := results[hits][pkgName].URL - latestVersion, err := getLatestVersion(url) + url := pkg.URL + var err error + availableVersion, err = getLatestVersion(url) if err != nil { return cli.Exit(color.RedString(err.Error()), 1) } - term.Printf(bold.Sprintf(" Latest Version:")+" %s\n", latestVersion) - + term.Printf(bold.Sprintf(" Available Version:")+" %s\n", availableVersion) + installedVersion, err = getVersionFromSystem(pkg.Name) + if err != nil { + return cli.Exit(color.RedString(err.Error()), 1) + } + if installedVersion != "" { + term.Printf(bold.Sprintf(" Installed Version:")+" %s\n", installedVersion) + } term.Printf(bold.Sprintf(" Description:")+" %s\n\n", cmd.Description) } } @@ -172,9 +185,14 @@ func searchPackages(ctx context.Context, keywords []string, packageList *package } if len(resultHits) > 0 { - term.Printf("\nInstall using \"%s\".\n", color.BlueString("%s install [package]", tools.Self())) + if installedVersion == "" { + term.Printf("\nInstall using \"%s\".\n", color.BlueString("%s install [package]", tools.Self())) + } else if installedVersion != availableVersion { + term.Printf("\nUpdate using \"%s\".\n", color.BlueString("%s update [package]", tools.Self())) + } else { + term.Printf(color.BlueString("Package is already up-to-date on your system")) + } } - return nil } @@ -230,3 +248,31 @@ type CommandObject struct { Version string `json:"version"` Description string `json:"description"` } + +func getVersionFromSystem(command string) (string, error) { + paths := filepath.SplitList(getPackageBinPaths()) + suffix := "cli-" + command + finalPath := "" + for _, path := range paths { + if strings.HasSuffix(path, suffix) { + finalPath = path + break + } + } + + if finalPath == "" { + return "", nil + } + body, err := os.ReadFile(filepath.Join(finalPath, "cli.json")) + if err != nil { + return "", fmt.Errorf("Error reading the file: %s", err.Error()) + + } + + var cli CLI + if err := json.Unmarshal(body, &cli); err != nil { + return "", fmt.Errorf("Error parsing the JSON: %s", err.Error()) + } + + return cli.CommandList[0].Version, nil +} diff --git a/pkg/commands/command_search_test.go b/pkg/commands/command_search_test.go index f8a020d..86845ef 100644 --- a/pkg/commands/command_search_test.go +++ b/pkg/commands/command_search_test.go @@ -5,12 +5,16 @@ import ( "net/http" "net/http/httptest" "os" + "path/filepath" "testing" - "github.com/akamai/cli/pkg/config" - "github.com/akamai/cli/pkg/terminal" + "github.com/akamai/cli/pkg/packages" "github.com/akamai/cli/pkg/tools" "github.com/fatih/color" + + "github.com/akamai/cli/pkg/config" + "github.com/akamai/cli/pkg/git" + "github.com/akamai/cli/pkg/terminal" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" @@ -19,136 +23,189 @@ import ( func TestCmdSearch(t *testing.T) { tests := map[string]struct { args []string - init func(*terminal.Mock) + init func(*mocked) packages *packageList withError string }{ - "search and find single package - sample": { + "search and find single package - sample when package is not installed": { args: []string{"sample"}, - init: func(m *terminal.Mock) { + init: func(m *mocked) { bold := color.New(color.FgWhite, color.Bold) - m.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{1}) + m.term.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{1}) - m.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"sample", color.BlueString("SAMPLE")}). + m.term.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"sample", color.BlueString("SAMPLE")}). Return().Once() - m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"sample", ""}). + m.term.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"sample", ""}). Return().Once() h := mockedServer("sample", "2.0.0", t) githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" + m.term.On("Printf", bold.Sprintf(" Available Version:")+" %s\n", []interface{}{"2.0.0"}).Return().Once() - m.On("Printf", bold.Sprintf(" Latest Version:")+" %s\n", []interface{}{"2.0.0"}).Return().Once() - m.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for single match"}). + m.term.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for single match"}). + Return().Once() + m.term.On("Printf", "\nInstall using \"%s\".\n", []interface{}{color.BlueString("%s install [package]", tools.Self())}). Return().Once() + }, + packages: packagesForTest, + }, + "search and find single package - echo when installed version is less than available version": { + args: []string{"echo-uninstall"}, + init: func(m *mocked) { + bold := color.New(color.FgWhite, color.Bold) + m.term.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{1}) - m.On("Printf", "\nInstall using \"%s\".\n", []interface{}{color.BlueString("%s install [package]", tools.Self())}). + m.term.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"echo", color.BlueString("echo")}). + Return().Once() + m.term.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"echo-uninstall", ""}). + Return().Once() + + h := mockedServer("echo-uninstall", "2.0.0", t) + githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" + + m.term.On("Printf", bold.Sprintf(" Available Version:")+" %s\n", []interface{}{"2.0.0"}).Return().Once() + + m.langManager.On("FindExec", packages.LanguageRequirements{Go: "1.14.0"}, "echo-uninstall").Return([]string{}, packages.ErrNoExeFound).Once() + m.langManager.On("GetPackageBinPaths").Return("/path/to/echo-uninstall").Once() + m.term.On("Printf", bold.Sprintf(" Installed Version:")+" %s\n", []interface{}{"1.0.0"}).Return().Once() + + m.term.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for single match"}). + Return().Once() + m.term.On("Printf", "\nUpdate using \"%s\".\n", []interface{}{color.BlueString("%s update [package]", tools.Self())}). + Return().Once() + }, + packages: packagesForTest, + }, + "search and find single package - echo when installed version is equal to available version": { + args: []string{"echo-uninstall"}, + init: func(m *mocked) { + bold := color.New(color.FgWhite, color.Bold) + m.term.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{1}) + + m.term.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"echo", color.BlueString("echo")}). + Return().Once() + m.term.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"echo-uninstall", ""}). + Return().Once() + + h := mockedServer("echo-uninstall", "1.0.0", t) + githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" + + m.term.On("Printf", bold.Sprintf(" Available Version:")+" %s\n", []interface{}{"1.0.0"}).Return().Once() + + m.langManager.On("FindExec", packages.LanguageRequirements{Go: "1.14.0"}, "echo-uninstall").Return([]string{}, packages.ErrNoExeFound).Once() + m.langManager.On("GetPackageBinPaths").Return("/path/to/echo-uninstall").Once() + m.term.On("Printf", bold.Sprintf(" Installed Version:")+" %s\n", []interface{}{"1.0.0"}).Return().Once() + + m.term.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for single match"}). + Return().Once() + m.term.On("Printf", color.BlueString("Package is already up-to-date on your system"), []interface{}(nil)). Return().Once() }, packages: packagesForTest, }, "search and find multiple packages - cli": { args: []string{"cli"}, - init: func(m *terminal.Mock) { + init: func(m *mocked) { bold := color.New(color.FgWhite, color.Bold) - m.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{5}) + m.term.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{5}) - m.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"CLI no cmd match", color.BlueString("cli-no-cmd-match")}). + m.term.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"CLI no cmd match", color.BlueString("cli-no-cmd-match")}). Return().Once() - m.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"abc-2", color.BlueString("cli-2")}). + m.term.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"abc-2", color.BlueString("cli-2")}). Return().Once() - m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"cli-2", "(aliases: abc, abc2)"}). + m.term.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"cli-2", "(aliases: abc, abc2)"}). Return().Once() h := mockedServer("cli-2", "1.0.0", t) githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" - m.On("Printf", bold.Sprintf(" Latest Version:")+" %s\n", []interface{}{"1.0.0"}). + m.term.On("Printf", bold.Sprintf(" Available Version:")+" %s\n", []interface{}{"1.0.0"}). Return().Once() - m.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for match on name"}). + m.term.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for match on name"}). Return().Once() - m.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"cli-1", color.BlueString("abc-1")}). + m.term.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"cli-1", color.BlueString("abc-1")}). Return().Once() - m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"ClI-1", ""}). + m.term.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"ClI-1", ""}). Return().Once() h = mockedServer("CLI-1", "1.0.0", t) githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" - m.On("Printf", bold.Sprintf(" Latest Version:")+" %s\n", []interface{}{"1.0.0"}). + m.term.On("Printf", bold.Sprintf(" Available Version:")+" %s\n", []interface{}{"1.0.0"}). Return().Once() - m.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for match on title"}). + m.term.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for match on title"}). Return().Once() - m.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"abc-5", color.BlueString("abc-5")}). + m.term.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"abc-5", color.BlueString("abc-5")}). Return().Once() - m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"cli", ""}). + m.term.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"cli", ""}). Return().Once() h = mockedServer("cli", "1.0.0", t) githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" - m.On("Printf", bold.Sprintf(" Latest Version:")+" %s\n", []interface{}{"1.0.0"}). + m.term.On("Printf", bold.Sprintf(" Available Version:")+" %s\n", []interface{}{"1.0.0"}). Return().Once() - m.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for match on command name"}). + m.term.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"test for match on command name"}). Return().Once() - m.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"abc-3", color.BlueString("abc-3")}). + m.term.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"abc-3", color.BlueString("abc-3")}). Return().Once() - m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"abc-3", ""}). + m.term.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"abc-3", ""}). Return().Once() h = mockedServer("abc-3", "1.0.0", t) githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" - m.On("Printf", bold.Sprintf(" Latest Version:")+" %s\n", []interface{}{"1.0.0"}). + m.term.On("Printf", bold.Sprintf(" Available Version:")+" %s\n", []interface{}{"1.0.0"}). Return().Once() - m.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"CLI - test for match on description"}). + m.term.On("Printf", bold.Sprintf(" Description:")+" %s\n\n", []interface{}{"CLI - test for match on description"}). Return().Once() - m.On("Printf", "\nInstall using \"%s\".\n", []interface{}{color.BlueString("%s install [package]", tools.Self())}). + m.term.On("Printf", "\nInstall using \"%s\".\n", []interface{}{color.BlueString("%s install [package]", tools.Self())}). Return().Once() }, packages: packagesForTest, }, "search with no results - terraform": { args: []string{"terraform"}, - init: func(m *terminal.Mock) { - m.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{0}) + init: func(m *mocked) { + m.term.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{0}) }, packages: packagesForTest, }, "no args passed": { args: []string{}, - init: func(m *terminal.Mock) {}, + init: func(m *mocked) {}, withError: "You must specify one or more keywords", }, "search and find single package - 404": { args: []string{"sample"}, - init: func(m *terminal.Mock) { + init: func(m *mocked) { bold := color.New(color.FgWhite, color.Bold) - m.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{1}) - - m.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"sample", color.BlueString("SAMPLE")}). + m.term.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{1}) + m.term.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"sample", color.BlueString("SAMPLE")}). Return().Once() - m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"sample", ""}). + m.term.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"sample", ""}). Return().Once() h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "Not Found", http.StatusNotFound) + })) - defer h.Close() + githubURLTemplate = h.URL + "/akamai/%s/master/cli.json" }, packages: packagesForTest, - withError: "error: status code 400", + withError: "error: status code 404", }, "search and find single package - when no latest version found": { args: []string{"sample"}, - init: func(m *terminal.Mock) { + init: func(m *mocked) { bold := color.New(color.FgWhite, color.Bold) - m.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{1}) + m.term.On("Printf", color.YellowString("Results Found:")+" %d\n\n", []interface{}{1}) - m.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"sample", color.BlueString("SAMPLE")}). + m.term.On("Printf", color.GreenString("Package: ")+"%s [%s]\n", []interface{}{"sample", color.BlueString("SAMPLE")}). Return().Once() - m.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"sample", ""}). + m.term.On("Printf", bold.Sprintf(" Command:")+" %s %s\n", []interface{}{"sample", ""}). Return().Once() h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -168,7 +225,8 @@ func TestCmdSearch(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - m := &mocked{&terminal.Mock{}, &config.Mock{}, nil, nil, nil} + require.NoError(t, os.Setenv("AKAMAI_CLI_HOME", filepath.Join(".", "testdata"))) + m := &mocked{&terminal.Mock{}, &config.Mock{}, &git.MockRepo{}, &packages.Mock{}, nil} pr := &mockPackageReader{} pr.On("readPackage").Return(test.packages.copy(t), nil).Once() @@ -184,7 +242,7 @@ func TestCmdSearch(t *testing.T) { args = append(args, "search") args = append(args, test.args...) - test.init(m.term) + test.init(m) err := app.RunContext(ctx, args) m.cfg.AssertExpectations(t) @@ -228,6 +286,20 @@ var packagesForTest = &packageList{ Node: "7.0.0", }, }, + { + Title: "echo", + Name: "echo", + Commands: []command{ + { + Name: "echo-uninstall", + Version: "1.0.0", + Description: "test for single match", + }, + }, + Requirements: requirements{ + Go: "1.14.0", + }, + }, { Title: "abc-2", Name: "cli-2", diff --git a/pkg/commands/package_list/package-list.json b/pkg/commands/package_list/package-list.json index b192561..74f0305 100644 --- a/pkg/commands/package_list/package-list.json +++ b/pkg/commands/package_list/package-list.json @@ -11,7 +11,7 @@ }, { "title": "API Gateway", - "name": "akamai/api-gateway", + "name": "api-gateway", "url": "https://github.com/akamai/cli-api-gateway", "issues": "https://github.com/akamai/cli-api-gateway/issues", "commands": [ From 6756dce09b30916e19e2aae1f6831a0c8c6eb473 Mon Sep 17 00:00:00 2001 From: Piotr Bartosik Date: Thu, 29 Aug 2024 11:31:15 +0000 Subject: [PATCH 9/9] DXE-4154 Update the changelog and version number for the release --- CHANGELOG.md | 14 ++++++++------ pkg/version/version.go | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da9d736..e6e3012 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,22 @@ # RELEASE NOTES -## X.X.X (X X, X) +## 1.6.0 (September 5, 2024) ### Enhancements -* Support to show the `Installed Version` of commands during `search` -* Removed commands and packages versions from `package-list.json` + +* Added support to show the `Installed Version` of commands during `search` +* Updated the list of packages in `packages-list.json` ([GH#192](https://github.com/akamai/cli/issues/192)) +* Removed versions of the packages from `package-list.json` * Changed package installation order - * Cli will first check if new binaries are available, if the package has no binaries or no valid binaries can be found, it will build the package locally. + * Cli will first check if new binaries are available. If the package has no binaries or no valid + binaries can be found, it will build the package locally * --force flag has been deprecated for both install and update * Migrated to go 1.21 * Updated various dependencies -* Enabled uninstall command when binaries were not found ### Fixes -* Updated the versions, descriptions, requirements and packages of the dependencies in the `packages-list.json` ([GH#192](https://github.com/akamai/cli/issues/192)) +* Fixed uninstalling of a command when binaries are not found ## 1.5.6 (January 22, 2024) diff --git a/pkg/version/version.go b/pkg/version/version.go index 4d47580..b95e952 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -4,7 +4,7 @@ import "github.com/Masterminds/semver" const ( // Version Application Version - Version = "1.5.6" + Version = "1.6.0" // Equals p1==p2 in version.Compare(p1, p2) Equals = 0 // Error failure parsing one of the parameters in version.Compare(p1, p2)