Skip to content

Commit 7c3254b

Browse files
committed
feat: add rust feature
1 parent bd71d50 commit 7c3254b

File tree

12 files changed

+381
-0
lines changed

12 files changed

+381
-0
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ jobs:
3535
"node",
3636
"playwright-deps",
3737
"python",
38+
"rust",
3839
"sonar-scanner-cli",
3940
"system-packages",
4041
"timezone",

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Below is a list with included features, click on the link for more details.
3535
| [node](./features/src/node/README.md) | Installs Node.js. |
3636
| [playwright-deps](./features/src/playwright-deps/README.md) | Installs all dependencies required to run Playwright. |
3737
| [python](./features/src/python/README.md) | Installs Python. |
38+
| [rust](./features/src/rust/README.md) | A package which installs Rust. |
3839
| [sonar-scanner-cli](./features/src/sonar-scanner-cli/README.md) | Installs the SonarScanner CLI. |
3940
| [system-packages](./features/src/system-packages/README.md) | Install arbitrary system packages using the system package manager. |
4041
| [timezone](./features/src/timezone/README.md) | Allows setting the timezone. |

build/build.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@ func init() {
180180
gotaskr.Task("Feature:python:Test", func() error { return testFeature("python") })
181181
gotaskr.Task("Feature:python:Publish", func() error { return publishFeature("python") })
182182

183+
////////// rust
184+
gotaskr.Task("Feature:rust:Package", func() error { return packageFeature("rust") })
185+
gotaskr.Task("Feature:rust:Test", func() error { return testFeature("rust") })
186+
gotaskr.Task("Feature:rust:Publish", func() error { return publishFeature("rust") })
187+
183188
////////// sonar-scanner-cli
184189
gotaskr.Task("Feature:sonar-scanner-cli:Package", func() error { return packageFeature("sonar-scanner-cli") })
185190
gotaskr.Task("Feature:sonar-scanner-cli:Test", func() error { return testFeature("sonar-scanner-cli") })

features/src/rust/NOTES.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
## Notes
2+
3+
### System Compatibility
4+
5+
Debian, Ubuntu
6+
7+
### Accessed Urls
8+
9+
Needs access to the following URL for downloading and resolving:
10+
* https://static.rust-lang.org

features/src/rust/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Rust (rust)
2+
3+
A package which installs Rust.
4+
5+
## Example Usage
6+
7+
```json
8+
"features": {
9+
"ghcr.io/postfinance/devcontainer-features/rust:0.1.0": {
10+
"version": "latest",
11+
"rustupVersion": "latest",
12+
"profile": "minimal",
13+
"components": "rustfmt,rust-analyzer,rust-src,clippy",
14+
"enableWindowsTarget": false
15+
}
16+
}
17+
```
18+
19+
## Options
20+
21+
| Option | Description | Type | Default Value | Proposals |
22+
|-----|-----|-----|-----|-----|
23+
| version | The version of Rust to install. | string | latest | latest, 1.76 |
24+
| rustupVersion | The version of rustup to install. | string | latest | latest, 1.27.1 |
25+
| profile | The rustup profile to install. | string | minimal | minimal, default, complete |
26+
| components | A comma separated list with components that should be installed. | string | rustfmt,rust-analyzer,rust-src,clippy | , rustfmt,rust-analyzer, rls,rust-analysis |
27+
| enableWindowsTarget | A flag to indicate if the Windows target (and needed tools) should be installed. | boolean | false | true, false |
28+
29+
## Customizations
30+
31+
### VS Code Extensions
32+
33+
- `vadimcn.vscode-lldb`
34+
- `rust-lang.rust-analyzer`
35+
- `tamasfe.even-better-toml`
36+
- `serayuzgur.crates`
37+
38+
## Notes
39+
40+
### System Compatibility
41+
42+
Debian, Ubuntu
43+
44+
### Accessed Urls
45+
46+
Needs access to the following URL for downloading and resolving:
47+
* https://static.rust-lang.org
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"id": "rust",
3+
"version": "0.1.0",
4+
"name": "Rust",
5+
"description": "A package which installs Rust.",
6+
"options": {
7+
"version": {
8+
"type": "string",
9+
"proposals": [
10+
"latest",
11+
"1.76"
12+
],
13+
"default": "latest",
14+
"description": "The version of Rust to install."
15+
},
16+
"rustupVersion": {
17+
"type": "string",
18+
"proposals": [
19+
"latest",
20+
"1.27.1"
21+
],
22+
"default": "latest",
23+
"description": "The version of rustup to install."
24+
},
25+
"profile": {
26+
"type": "string",
27+
"proposals": [
28+
"minimal",
29+
"default",
30+
"complete"
31+
],
32+
"default": "minimal",
33+
"description": "The rustup profile to install."
34+
},
35+
"components": {
36+
"type": "string",
37+
"proposals": [
38+
"",
39+
"rustfmt,rust-analyzer",
40+
"rls,rust-analysis"
41+
],
42+
"default": "rustfmt,rust-analyzer,rust-src,clippy",
43+
"description": "A comma separated list with components that should be installed."
44+
},
45+
"enableWindowsTarget": {
46+
"type": "boolean",
47+
"default": false,
48+
"description": "A flag to indicate if the Windows target (and needed tools) should be installed."
49+
}
50+
},
51+
"customizations": {
52+
"vscode": {
53+
"extensions": [
54+
"vadimcn.vscode-lldb",
55+
"rust-lang.rust-analyzer",
56+
"tamasfe.even-better-toml",
57+
"serayuzgur.crates"
58+
]
59+
}
60+
},
61+
"containerEnv": {
62+
"CARGO_HOME": "/usr/local/cargo",
63+
"RUSTUP_HOME": "/usr/local/rustup",
64+
"PATH": "/usr/local/cargo/bin:${PATH}"
65+
}
66+
}

features/src/rust/install.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
. ./functions.sh
2+
3+
"./installer_$(detect_arch)" \
4+
-version="${VERSION:-"latest"}" \
5+
-rustupVersion="${RUSTUPVERSION:-"latest"}" \
6+
-rustupDownloadUrl="${RUSTUPDOWNLOADURL:-""}" \
7+
-profile="${PROFILE:-"minimal"}" \
8+
-components="${COMPONENTS:-"rustfmt,rust-analyzer,rust-src,clippy"}" \
9+
-enableWindowsTarget="${ENABLEWINDOWSTARGET:-"false"}"

features/src/rust/installer.go

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package main
2+
3+
import (
4+
"builder/installer"
5+
"flag"
6+
"fmt"
7+
"os"
8+
"regexp"
9+
"strings"
10+
11+
"github.com/roemer/gotaskr/execr"
12+
"github.com/roemer/gover"
13+
)
14+
15+
//////////
16+
// Configuration
17+
//////////
18+
19+
// Regex with 2-3 digits like 1.0 or 1.79.0
20+
var threeDigitRegex *regexp.Regexp = regexp.MustCompile(`^?(\d+)\.(\d+)(?:\.(\d+))?$`)
21+
22+
// Full Regex versioning, like 1.0.0-alpha.2
23+
var semVerRegex *regexp.Regexp = regexp.MustCompile(`^?(\d+)\.(\d+)(?:\.(\d+))?(?:-([a-z]+)(?:\.?(\d+))?)?$`)
24+
25+
//////////
26+
// Main
27+
//////////
28+
29+
func main() {
30+
if err := runMain(); err != nil {
31+
fmt.Printf("Error: %v\n", err)
32+
os.Exit(1)
33+
}
34+
}
35+
36+
func runMain() error {
37+
// Handle the flags
38+
version := flag.String("version", "lts", "")
39+
rustupVersion := flag.String("rustupVersion", "latest", "")
40+
profile := flag.String("profile", "minimal", "")
41+
components := flag.String("components", "rustfmt,rust-analyzer,rust-src,clippy", "")
42+
enableWindowsTarget := flag.Bool("enableWindowsTarget", false, "")
43+
flag.Parse()
44+
45+
// Create and process the feature
46+
feature := installer.NewFeature("PF Rust", true,
47+
&rustupComponent{
48+
ComponentBase: installer.NewComponentBase("rustup", *rustupVersion),
49+
profile: *profile,
50+
},
51+
&rustComponent{
52+
ComponentBase: installer.NewComponentBase("rust", *version),
53+
components: *components,
54+
profile: *profile,
55+
},
56+
&buildEssentialComponent{
57+
ComponentBase: installer.NewComponentBase("build-essential", installer.VERSION_SYSTEM_DEFAULT),
58+
},
59+
)
60+
// Optional component
61+
if *enableWindowsTarget {
62+
feature.AddComponents(&windowsTargetComponent{
63+
ComponentBase: installer.NewComponentBase("windows-target", installer.VERSION_IRRELEVANT),
64+
})
65+
}
66+
// Last component
67+
feature.AddComponents(&permissionsComponent{
68+
ComponentBase: installer.NewComponentBase("permissions", installer.VERSION_IRRELEVANT),
69+
})
70+
return feature.Process()
71+
}
72+
73+
//////////
74+
// Implementation
75+
//////////
76+
77+
type rustupComponent struct {
78+
*installer.ComponentBase
79+
profile string
80+
}
81+
82+
func (c *rustupComponent) GetAllVersions() ([]*gover.Version, error) {
83+
allTags, err := installer.Tools.GitHub.GetTags("rust-lang", "rustup")
84+
if err != nil {
85+
return nil, err
86+
}
87+
return installer.Tools.Versioning.ParseVersionsFromList(allTags, threeDigitRegex, true)
88+
}
89+
90+
func (c *rustupComponent) InstallVersion(version *gover.Version) error {
91+
// Download the file
92+
archPart, err := installer.Tools.System.MapArchitecture(map[string]string{
93+
installer.AMD64: "x86_64",
94+
installer.ARM64: "aarch64",
95+
})
96+
if err != nil {
97+
return err
98+
}
99+
fileName := "rustup-init"
100+
downloadUrl := fmt.Sprintf("https://static.rust-lang.org/rustup/archive/%s/%s-unknown-linux-gnu/rustup-init", version.Raw, archPart)
101+
if err := installer.Tools.Download.ToFile(downloadUrl, fileName, "Rustup-Init"); err != nil {
102+
return err
103+
}
104+
// Install it
105+
if err := os.Chmod(fileName, os.ModePerm); err != nil {
106+
return err
107+
}
108+
if err := execr.Run(true, "./"+fileName, "-y", "--default-toolchain", "none", "--no-modify-path", "--profile", c.profile); err != nil {
109+
return err
110+
}
111+
// Cleanup
112+
if err := os.Remove(fileName); err != nil {
113+
return err
114+
}
115+
return nil
116+
}
117+
118+
type rustComponent struct {
119+
*installer.ComponentBase
120+
profile string
121+
components string
122+
}
123+
124+
func (c *rustComponent) GetAllVersions() ([]*gover.Version, error) {
125+
allTags, err := installer.Tools.GitHub.GetTags("rust-lang", "rust")
126+
if err != nil {
127+
return nil, err
128+
}
129+
return installer.Tools.Versioning.ParseVersionsFromList(allTags, semVerRegex, true)
130+
}
131+
132+
func (c *rustComponent) InstallVersion(version *gover.Version) error {
133+
// Install it
134+
if err := execr.Run(true, "rustup", "toolchain", "install", "--profile", c.profile, "--no-self-update", version.Raw); err != nil {
135+
return err
136+
}
137+
// Installing the components
138+
fmt.Printf("Installing components: %s\n", c.components)
139+
args := []string{
140+
"component",
141+
"add",
142+
}
143+
for _, component := range strings.Split(c.components, ",") {
144+
trimmed := strings.TrimSpace(component)
145+
if trimmed != "" {
146+
args = append(args, trimmed)
147+
}
148+
}
149+
if err := execr.Run(true, "rustup", args...); err != nil {
150+
return err
151+
}
152+
return nil
153+
}
154+
155+
type buildEssentialComponent struct {
156+
*installer.ComponentBase
157+
}
158+
159+
func (c *buildEssentialComponent) InstallVersion(version *gover.Version) error {
160+
return installer.Tools.System.InstallPackages("build-essential")
161+
}
162+
163+
type windowsTargetComponent struct {
164+
*installer.ComponentBase
165+
}
166+
167+
func (c *windowsTargetComponent) InstallVersion(version *gover.Version) error {
168+
if err := execr.Run(true, "rustup", "target", "add", "x86_64-pc-windows-gnu"); err != nil {
169+
return err
170+
}
171+
if err := installer.Tools.System.InstallPackages("mingw-w64"); err != nil {
172+
return err
173+
}
174+
return nil
175+
}
176+
177+
type permissionsComponent struct {
178+
*installer.ComponentBase
179+
}
180+
181+
func (c *permissionsComponent) InstallVersion(version *gover.Version) error {
182+
return execr.Run(true, "chmod", "-R", "777", os.Getenv("RUSTUP_HOME"), os.Getenv("CARGO_HOME"))
183+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
set -e
3+
4+
[[ -f "$(dirname "$0")/../functions.sh" ]] && source "$(dirname "$0")/../functions.sh"
5+
[[ -f "$(dirname "$0")/functions.sh" ]] && source "$(dirname "$0")/functions.sh"
6+
7+
check_version "$(rustup --version 2>/dev/null)" "rustup 1.27.1 (54dd3d00f 2024-04-24)"
8+
check_version "$(rustc --version)" "rustc 1.76.0 (07dca489a 2024-02-04)"
9+
check_version "$(rustup target list --installed)" $'x86_64-pc-windows-gnu\nx86_64-unknown-linux-gnu'
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
set -e
3+
4+
[[ -f "$(dirname "$0")/../functions.sh" ]] && source "$(dirname "$0")/../functions.sh"
5+
[[ -f "$(dirname "$0")/functions.sh" ]] && source "$(dirname "$0")/functions.sh"
6+
7+
check_version "$(rustup --version 2>/dev/null)" "rustup 1.27.1 (54dd3d00f 2024-04-24)"
8+
check_version "$(rustc --version)" "rustc 1.76.0 (07dca489a 2024-02-04)"
9+
check_version "$(rustup target list --installed)" $'x86_64-unknown-linux-gnu'

0 commit comments

Comments
 (0)