Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: download arm64 CLI on M1/M2 macs, allow baseURL configuration [HEAD-867] #453

Merged
merged 6 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Snyk Changelog

## [2.5.4]

### Added

- support for arm64 CLI on macOS
- support to configure base URL for CLI downloads

## [2.5.3]

### Fixed
Expand Down
13 changes: 7 additions & 6 deletions src/main/kotlin/io/snyk/plugin/cli/Platform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,24 @@ class Platform(val snykWrapperFileName: String) {
val LINUX = Platform("snyk-linux")
val LINUX_ALPINE = Platform("snyk-alpine")
val MAC_OS = Platform("snyk-macos")
val MAC_OS_ARM64 = Platform("snyk-macos-arm64")
val WINDOWS = Platform("snyk-win.exe")

@Throws(PlatformDetectionException::class)
fun current(): Platform = detect(System.getProperties())

@Suppress("MoveVariableDeclarationIntoWhen")
@Throws(PlatformDetectionException::class)
fun detect(systemProperties: Properties): Platform {
val architectureName = (systemProperties["os.name"] as String).lowercase()
return when (architectureName) {
val osName = (systemProperties["os.name"] as String).lowercase()
val archName = (systemProperties["os.arch"] as String).lowercase()
return when (osName) {
"linux" -> if (Paths.get("/etc/alpine-release").toFile().exists()) LINUX_ALPINE else LINUX
"mac os x", "darwin", "osx" -> MAC_OS
"mac os x", "darwin", "osx" -> if (archName != "aarch64") MAC_OS else MAC_OS_ARM64
else -> {
if (architectureName.contains("windows")) {
if (osName.contains("windows")) {
WINDOWS
} else {
throw PlatformDetectionException("$architectureName is not supported CPU type")
throw PlatformDetectionException("$osName is not supported CPU type")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import java.util.UUID
)
class SnykApplicationSettingsStateService : PersistentStateComponent<SnykApplicationSettingsStateService> {

var cliBaseDownloadURL: String = "https://static.snyk.io"
var cliPath: String = getPluginPath() + separator + Platform.current().snykWrapperFileName
var manageBinariesAutomatically: Boolean = true
var fileListenerEnabled: Boolean = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.snyk.plugin.services.download

import com.intellij.openapi.progress.ProgressIndicator
import io.snyk.plugin.cli.Platform
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.services.download.HttpRequestHelper.createRequest
import java.io.File
import java.nio.file.AtomicMoveNotSupportedException
Expand All @@ -13,10 +14,14 @@ import javax.xml.bind.DatatypeConverter

class CliDownloader {
companion object {
const val BASE_URL = "https://static.snyk.io"
const val LATEST_RELEASES_URL = "$BASE_URL/cli/latest/version"
val LATEST_RELEASE_DOWNLOAD_URL = "$BASE_URL/cli/latest/${Platform.current().snykWrapperFileName}"
val SHA256_DOWNLOAD_URL = "$BASE_URL/cli/latest/${Platform.current().snykWrapperFileName}.sha256"
val BASE_URL: String
get() = pluginSettings().cliBaseDownloadURL
val LATEST_RELEASES_URL: String
get() = "$BASE_URL/cli/latest/version"
val LATEST_RELEASE_DOWNLOAD_URL: String
get() = "$BASE_URL/cli/latest/${Platform.current().snykWrapperFileName}"
val SHA256_DOWNLOAD_URL: String
get() = "$BASE_URL/cli/latest/${Platform.current().snykWrapperFileName}.sha256"
}

fun calculateSha256(bytes: ByteArray): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ class SnykProjectSettingsConfigurable(val project: Project) : SearchableConfigur
snykSettingsDialog.isScanTypeChanged() ||
snykSettingsDialog.isSeverityEnablementChanged() ||
snykSettingsDialog.manageBinariesAutomatically() != settingsStateService.manageBinariesAutomatically ||
snykSettingsDialog.getCliPath() != settingsStateService.cliPath
snykSettingsDialog.getCliPath() != settingsStateService.cliPath ||
snykSettingsDialog.getCliBaseDownloadURL() != settingsStateService.cliBaseDownloadURL

private fun isCoreParamsModified() = isTokenModified() ||
isCustomEndpointModified() ||
Expand Down Expand Up @@ -83,6 +84,7 @@ class SnykProjectSettingsConfigurable(val project: Project) : SearchableConfigur

settingsStateService.manageBinariesAutomatically = snykSettingsDialog.manageBinariesAutomatically()
settingsStateService.cliPath = snykSettingsDialog.getCliPath().trim()
settingsStateService.cliBaseDownloadURL = snykSettingsDialog.getCliBaseDownloadURL().trim()

snykSettingsDialog.saveScanTypeChanges()
snykSettingsDialog.saveSeveritiesEnablementChanges()
Expand Down
12 changes: 12 additions & 0 deletions src/main/kotlin/io/snyk/plugin/ui/SnykSettingsDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class SnykSettingsDialog(

private val manageBinariesAutomatically: JCheckBox = JCheckBox()
private val cliPathTextBoxWithFileBrowser = TextFieldWithBrowseButton()
private val cliBaseDownloadUrlTextField = JTextField()

private val logger = Logger.getInstance(this::class.java)

Expand Down Expand Up @@ -126,6 +127,7 @@ class SnykSettingsDialog(
manageBinariesAutomatically.isSelected = applicationSettings.manageBinariesAutomatically

cliPathTextBoxWithFileBrowser.text = applicationSettings.cliPath
cliBaseDownloadUrlTextField.text = applicationSettings.cliBaseDownloadURL
additionalParametersTextField.text = applicationSettings.getAdditionalParameters(project)
}
}
Expand Down Expand Up @@ -463,6 +465,15 @@ class SnykSettingsDialog(
)
)

cliBaseDownloadUrlTextField.toolTipText = "The default URL is https://static.snyk.io. " +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: do we really want to mention this here? From a maintenance perspective, I think it would be better to leave this to the user-docs.

"for FIPS-enabled CLIs (only available for Windows and Linux), please use https://static.snyk.io/fips"
executableSettingsPanel.add(
UI.PanelFactory
.panel(cliBaseDownloadUrlTextField)
.withLabel("Base URL to download the CLI: ").createPanel(),
gb.nextLine()
)

cliPathTextBoxWithFileBrowser.toolTipText = "The default path is ${getCliFile().canonicalPath}."
val descriptor = FileChooserDescriptor(true, false, false, false, false, false)
cliPathTextBoxWithFileBrowser.addBrowseFolderListener(
Expand Down Expand Up @@ -564,4 +575,5 @@ class SnykSettingsDialog(

fun getCliPath(): String = cliPathTextBoxWithFileBrowser.text
fun manageBinariesAutomatically() = manageBinariesAutomatically.isSelected
fun getCliBaseDownloadURL(): String = cliBaseDownloadUrlTextField.text
}
12 changes: 9 additions & 3 deletions src/test/kotlin/io/snyk/plugin/cli/PlatformTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package io.snyk.plugin.cli

import org.junit.Assert.assertTrue
import org.junit.Test
import java.util.*
import java.util.Properties

class PlatformTest {

@Test
fun testDetectPlatform() {
val properties = Properties()
properties["os.arch"] = "something"

properties["os.name"] = "linux"

Expand All @@ -18,12 +19,16 @@ class PlatformTest {
properties["os.name"] = "mac os x"
assertTrue(Platform.MAC_OS == Platform.detect(properties))

properties["os.name"] = "darwin"
properties["os.name"] = "osx"
assertTrue(Platform.MAC_OS == Platform.detect(properties))

properties["os.name"] = "osx"
properties["os.name"] = "darwin"
assertTrue(Platform.MAC_OS == Platform.detect(properties))

properties["os.name"] = "darwin"
properties["os.arch"] = "aarch64"
assertTrue(Platform.MAC_OS_ARM64 == Platform.detect(properties))

properties["os.name"] = "windows"
assertTrue(Platform.WINDOWS == Platform.detect(properties))
}
Expand All @@ -32,6 +37,7 @@ class PlatformTest {
fun testDetectPlatformException() {
val properties = Properties()
properties["os.name"] = "Not supported CPU type"
properties["os.arch"] = "dont care"

Platform.detect(properties)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class CliDownloaderServiceIntegTest : LightPlatformTestCase() {

every { downloader.calculateSha256(any()) } returns "wrong-sha"
justRun { errorHandler.handleChecksumVerificationException(any(), any(), any()) }
// this is needed, but I don't know why the config is not picked up from setUp()
every { pluginSettings() } returns SnykApplicationSettingsStateService()

cutSpy.downloadLatestRelease(indicator, project)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class CliDownloaderTest {
val expectedSha = cut.calculateSha256("wrong sha".toByteArray())

mockCliDownload()

every { pluginSettings() } returns SnykApplicationSettingsStateService()
try {
cut.downloadFile(testFile, expectedSha, mockk(relaxed = true))
fail("Should have thrown ChecksumVerificationException")
Expand Down
Loading