Skip to content
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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
"node",
"playwright-deps",
"python",
"rust",
"sonar-scanner-cli",
"system-packages",
"timezone",
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Below is a list with included features, click on the link for more details.
| [node](./features/src/node/README.md) | Installs Node.js. |
| [playwright-deps](./features/src/playwright-deps/README.md) | Installs all dependencies required to run Playwright. |
| [python](./features/src/python/README.md) | Installs Python. |
| [rust](./features/src/rust/README.md) | A package which installs Rust, common Rust utilities and their required dependencies. |
| [sonar-scanner-cli](./features/src/sonar-scanner-cli/README.md) | Installs the SonarScanner CLI. |
| [system-packages](./features/src/system-packages/README.md) | Install arbitrary system packages using the system package manager. |
| [timezone](./features/src/timezone/README.md) | Allows setting the timezone. |
Expand Down
5 changes: 5 additions & 0 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ func init() {
gotaskr.Task("Feature:python:Test", func() error { return testFeature("python") })
gotaskr.Task("Feature:python:Publish", func() error { return publishFeature("python") })

////////// rust
gotaskr.Task("Feature:rust:Package", func() error { return packageFeature("rust") })
gotaskr.Task("Feature:rust:Test", func() error { return testFeature("rust") })
gotaskr.Task("Feature:rust:Publish", func() error { return publishFeature("rust") })

////////// sonar-scanner-cli
gotaskr.Task("Feature:sonar-scanner-cli:Package", func() error { return packageFeature("sonar-scanner-cli") })
gotaskr.Task("Feature:sonar-scanner-cli:Test", func() error { return testFeature("sonar-scanner-cli") })
Expand Down
10 changes: 10 additions & 0 deletions features/src/rust/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## Notes

### System Compatibility

Debian, Ubuntu

### Accessed Urls

Needs access to the following URL for downloading and resolving:
* https://static.rust-lang.org
47 changes: 47 additions & 0 deletions features/src/rust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Rust (rust)

A package which installs Rust, common Rust utilities and their required dependencies.

## Example Usage

```json
"features": {
"ghcr.io/postfinance/devcontainer-features/rust:0.1.0": {
"version": "latest",
"rustupVersion": "latest",
"profile": "minimal",
"components": "rustfmt,rust-analyzer,rust-src,clippy",
"enableWindowsTarget": false
}
}
```

## Options

| Option | Description | Type | Default Value | Proposals |
|-----|-----|-----|-----|-----|
| version | The version of Rust to install. | string | latest | latest, 1.93.0 |
| rustupVersion | The version of rustup to install. | string | latest | latest, 1.27.1 |
| profile | The rustup profile to install. | string | minimal | minimal, default, complete |
| components | A comma separated list with components that should be installed. | string | rustfmt,rust-analyzer,rust-src,clippy | , rustfmt,rust-analyzer, rls,rust-analysis |
| enableWindowsTarget | A flag to indicate if the Windows target (and needed tools) should be installed. | boolean | false | true, false |

## Customizations

### VS Code Extensions

- `vadimcn.vscode-lldb`
- `rust-lang.rust-analyzer`
- `tamasfe.even-better-toml`
- `serayuzgur.crates`

## Notes

### System Compatibility

Debian, Ubuntu

### Accessed Urls

Needs access to the following URL for downloading and resolving:
* https://static.rust-lang.org
66 changes: 66 additions & 0 deletions features/src/rust/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"id": "rust",
"version": "0.1.0",
"name": "Rust",
"description": "A package which installs Rust, common Rust utilities and their required dependencies.",
"options": {
"version": {
"type": "string",
"proposals": [
"latest",
"1.93.0"
],
"default": "latest",
"description": "The version of Rust to install."
},
"rustupVersion": {
"type": "string",
"proposals": [
"latest",
"1.27.1"
],
"default": "latest",
"description": "The version of rustup to install."
},
"profile": {
"type": "string",
"proposals": [
"minimal",
"default",
"complete"
],
"default": "minimal",
"description": "The rustup profile to install."
},
"components": {
"type": "string",
"proposals": [
"",
"rustfmt,rust-analyzer",
"rls,rust-analysis"
],
"default": "rustfmt,rust-analyzer,rust-src,clippy",
"description": "A comma separated list with components that should be installed."
},
"enableWindowsTarget": {
"type": "boolean",
"default": false,
"description": "A flag to indicate if the Windows target (and needed tools) should be installed."
}
},
"customizations": {
"vscode": {
"extensions": [
"vadimcn.vscode-lldb",
"rust-lang.rust-analyzer",
"tamasfe.even-better-toml",
"serayuzgur.crates"
]
}
},
"containerEnv": {
"CARGO_HOME": "/usr/local/cargo",
"RUSTUP_HOME": "/usr/local/rustup",
"PATH": "/usr/local/cargo/bin:${PATH}"
}
}
8 changes: 8 additions & 0 deletions features/src/rust/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
. ./functions.sh

"./installer_$(detect_arch)" \
-version="${VERSION:-"latest"}" \
-rustupVersion="${RUSTUPVERSION:-"latest"}" \
-profile="${PROFILE:-"minimal"}" \
-components="${COMPONENTS:-"rustfmt,rust-analyzer,rust-src,clippy"}" \
-enableWindowsTarget="${ENABLEWINDOWSTARGET:-"false"}"
183 changes: 183 additions & 0 deletions features/src/rust/installer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package main

import (
"builder/installer"
"flag"
"fmt"
"os"
"regexp"
"strings"

"github.com/roemer/gotaskr/execr"
"github.com/roemer/gover"
)

//////////
// Configuration
//////////

// Regex with 2-3 digits like 1.0 or 1.79.0
var threeDigitRegex *regexp.Regexp = regexp.MustCompile(`^?(\d+)\.(\d+)(?:\.(\d+))?$`)

// Full Regex versioning, like 1.0.0-alpha.2
var semVerRegex *regexp.Regexp = regexp.MustCompile(`^?(\d+)\.(\d+)(?:\.(\d+))?(?:-([a-z]+)(?:\.?(\d+))?)?$`)

//////////
// Main
//////////

func main() {
if err := runMain(); err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
}

func runMain() error {
// Handle the flags
version := flag.String("version", "lts", "")
rustupVersion := flag.String("rustupVersion", "latest", "")
profile := flag.String("profile", "minimal", "")
components := flag.String("components", "rustfmt,rust-analyzer,rust-src,clippy", "")
enableWindowsTarget := flag.Bool("enableWindowsTarget", false, "")
flag.Parse()

// Create and process the feature
feature := installer.NewFeature("Rust", true,
&rustupComponent{
ComponentBase: installer.NewComponentBase("rustup", *rustupVersion),
profile: *profile,
},
&rustComponent{
ComponentBase: installer.NewComponentBase("rust", *version),
components: *components,
profile: *profile,
},
&buildEssentialComponent{
ComponentBase: installer.NewComponentBase("build-essential", installer.VERSION_SYSTEM_DEFAULT),
},
)
// Optional component
if *enableWindowsTarget {
feature.AddComponents(&windowsTargetComponent{
ComponentBase: installer.NewComponentBase("windows-target", installer.VERSION_IRRELEVANT),
})
}
// Last component
feature.AddComponents(&permissionsComponent{
ComponentBase: installer.NewComponentBase("permissions", installer.VERSION_IRRELEVANT),
})
return feature.Process()
}

//////////
// Implementation
//////////

type rustupComponent struct {
*installer.ComponentBase
profile string
}

func (c *rustupComponent) GetAllVersions() ([]*gover.Version, error) {
allTags, err := installer.Tools.GitHub.GetTags("rust-lang", "rustup")
if err != nil {
return nil, err
}
return installer.Tools.Versioning.ParseVersionsFromList(allTags, threeDigitRegex, true)
}

func (c *rustupComponent) InstallVersion(version *gover.Version) error {
// Download the file
archPart, err := installer.Tools.System.MapArchitecture(map[string]string{
installer.AMD64: "x86_64",
installer.ARM64: "aarch64",
})
if err != nil {
return err
}
fileName := "rustup-init"
downloadUrl := fmt.Sprintf("https://static.rust-lang.org/rustup/archive/%s/%s-unknown-linux-gnu/rustup-init", version.Raw, archPart)
if err := installer.Tools.Download.ToFile(downloadUrl, fileName, "Rustup-Init"); err != nil {
return err
}
// Install it
if err := os.Chmod(fileName, os.ModePerm); err != nil {
return err
}
if err := execr.Run(true, "./"+fileName, "-y", "--default-toolchain", "none", "--no-modify-path", "--profile", c.profile); err != nil {
return err
}
// Cleanup
if err := os.Remove(fileName); err != nil {
return err
}
return nil
}

type rustComponent struct {
*installer.ComponentBase
profile string
components string
}

func (c *rustComponent) GetAllVersions() ([]*gover.Version, error) {
allTags, err := installer.Tools.GitHub.GetTags("rust-lang", "rust")
if err != nil {
return nil, err
}
return installer.Tools.Versioning.ParseVersionsFromList(allTags, semVerRegex, true)
}

func (c *rustComponent) InstallVersion(version *gover.Version) error {
// Install it
if err := execr.Run(true, "rustup", "toolchain", "install", "--profile", c.profile, "--no-self-update", version.Raw); err != nil {
return err
}
// Installing the components
fmt.Printf("Installing components: %s\n", c.components)
args := []string{
"component",
"add",
}
for _, component := range strings.Split(c.components, ",") {
trimmed := strings.TrimSpace(component)
if trimmed != "" {
args = append(args, trimmed)
}
}
if err := execr.Run(true, "rustup", args...); err != nil {
return err
}
return nil
}

type buildEssentialComponent struct {
*installer.ComponentBase
}

func (c *buildEssentialComponent) InstallVersion(version *gover.Version) error {
return installer.Tools.System.InstallPackages("build-essential")
}

type windowsTargetComponent struct {
*installer.ComponentBase
}

func (c *windowsTargetComponent) InstallVersion(version *gover.Version) error {
if err := execr.Run(true, "rustup", "target", "add", "x86_64-pc-windows-gnu"); err != nil {
return err
}
if err := installer.Tools.System.InstallPackages("mingw-w64"); err != nil {
return err
}
return nil
}

type permissionsComponent struct {
*installer.ComponentBase
}

func (c *permissionsComponent) InstallVersion(version *gover.Version) error {
return execr.Run(true, "chmod", "-R", "777", os.Getenv("RUSTUP_HOME"), os.Getenv("CARGO_HOME"))
}
9 changes: 9 additions & 0 deletions features/test/rust/install-with-windows-target.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
set -e

[[ -f "$(dirname "$0")/../functions.sh" ]] && source "$(dirname "$0")/../functions.sh"
[[ -f "$(dirname "$0")/functions.sh" ]] && source "$(dirname "$0")/functions.sh"

check_version "$(rustup --version 2>/dev/null)" "rustup 1.27.1 (54dd3d00f 2024-04-24)"
check_version "$(rustc --version)" "rustc 1.76.0 (07dca489a 2024-02-04)"
check_version "$(rustup target list --installed)" $'x86_64-pc-windows-gnu\nx86_64-unknown-linux-gnu'
9 changes: 9 additions & 0 deletions features/test/rust/install-without-windows-target.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
set -e

[[ -f "$(dirname "$0")/../functions.sh" ]] && source "$(dirname "$0")/../functions.sh"
[[ -f "$(dirname "$0")/functions.sh" ]] && source "$(dirname "$0")/functions.sh"

check_version "$(rustup --version 2>/dev/null)" "rustup 1.27.1 (54dd3d00f 2024-04-24)"
check_version "$(rustc --version)" "rustc 1.76.0 (07dca489a 2024-02-04)"
check_version "$(rustup target list --installed)" $'x86_64-unknown-linux-gnu'
Loading
Loading