diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf25336..47d93b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,7 @@ jobs: "dotnet", "eclipse-deps", "git-lfs", + "github-copilot-cli", "gitlab-cli", "go", "gonovate", diff --git a/README.md b/README.md index d5fd14a..6eb04d8 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Below is a list with included features, click on the link for more details. | [dotnet](./features/src/dotnet/README.md) | A package which installs .NET SDKs, runtimes and workloads. | | [eclipse-deps](./features/src/eclipse-deps/README.md) | Installs all dependencies required to run the Eclipse IDE. | | [git-lfs](./features/src/git-lfs/README.md) | Installs Git LFS. | +| [github-copilot-cli](./features/src/github-copilot-cli/README.md) | Installs GitHub Copilot CLI (copilot), the AI-powered coding assistant for the terminal. | | [gitlab-cli](./features/src/gitlab-cli/README.md) | Installs the GitLab CLI. | | [go](./features/src/go/README.md) | Installs Go. | | [gonovate](./features/src/gonovate/README.md) | Installs Gonovate. | diff --git a/build/build.go b/build/build.go index 05f99c6..a63618f 100644 --- a/build/build.go +++ b/build/build.go @@ -110,6 +110,10 @@ func init() { gotaskr.Task("Feature:git-lfs:Package", func() error { return packageFeature("git-lfs") }) gotaskr.Task("Feature:git-lfs:Test", func() error { return testFeature("git-lfs") }) + ////////// github-copilot-cli + gotaskr.Task("Feature:github-copilot-cli:Package", func() error { return packageFeature("github-copilot-cli") }) + gotaskr.Task("Feature:github-copilot-cli:Test", func() error { return testFeature("github-copilot-cli") }) + ////////// gitlab-cli gotaskr.Task("Feature:gitlab-cli:Package", func() error { return packageFeature("gitlab-cli") }) gotaskr.Task("Feature:gitlab-cli:Test", func() error { return testFeature("gitlab-cli") }) diff --git a/features/src/github-copilot-cli/NOTES.md b/features/src/github-copilot-cli/NOTES.md new file mode 100644 index 0000000..d0ef26a --- /dev/null +++ b/features/src/github-copilot-cli/NOTES.md @@ -0,0 +1,13 @@ +## Notes + +### System Compatibility + +Debian, Ubuntu + +### Accessed Urls + +Needs access to the following URL for downloading: +* https://github.com + +Needs access to the following URL for resolving: +* https://api.github.com diff --git a/features/src/github-copilot-cli/README.md b/features/src/github-copilot-cli/README.md new file mode 100755 index 0000000..93f560a --- /dev/null +++ b/features/src/github-copilot-cli/README.md @@ -0,0 +1,35 @@ +# GitHub Copilot CLI (github-copilot-cli) + +Installs GitHub Copilot CLI (copilot), the AI-powered coding assistant for the terminal. + +## Example Usage + +```json +"features": { + "ghcr.io/postfinance/devcontainer-features/github-copilot-cli:1.0.0": { + "version": "latest", + "downloadUrl": "" + } +} +``` + +## Options + +| Option | Description | Type | Default Value | Proposals | +|-----|-----|-----|-----|-----| +| version | The version of GitHub Copilot CLI to install. | string | latest | latest, 1.0.39 | +| downloadUrl | The download URL to use for GitHub Copilot CLI binaries. | string | <empty> | https://mycompany.com/artifactory/github-releases-remote | + +## Notes + +### System Compatibility + +Debian, Ubuntu + +### Accessed Urls + +Needs access to the following URL for downloading: +* https://github.com + +Needs access to the following URL for resolving: +* https://api.github.com diff --git a/features/src/github-copilot-cli/devcontainer-feature.json b/features/src/github-copilot-cli/devcontainer-feature.json new file mode 100644 index 0000000..b955250 --- /dev/null +++ b/features/src/github-copilot-cli/devcontainer-feature.json @@ -0,0 +1,25 @@ +{ + "id": "github-copilot-cli", + "version": "1.0.0", + "name": "GitHub Copilot CLI", + "description": "Installs GitHub Copilot CLI (copilot), the AI-powered coding assistant for the terminal.", + "options": { + "version": { + "type": "string", + "proposals": [ + "latest", + "1.0.39" + ], + "default": "latest", + "description": "The version of GitHub Copilot CLI to install." + }, + "downloadUrl": { + "type": "string", + "default": "", + "proposals": [ + "https://mycompany.com/artifactory/github-releases-remote" + ], + "description": "The download URL to use for GitHub Copilot CLI binaries." + } + } +} diff --git a/features/src/github-copilot-cli/install.sh b/features/src/github-copilot-cli/install.sh new file mode 100644 index 0000000..e2e3b0a --- /dev/null +++ b/features/src/github-copilot-cli/install.sh @@ -0,0 +1,5 @@ +. ./functions.sh + +"./installer_$(detect_arch)" \ + -version="${VERSION:-"latest"}" \ + -downloadUrl="${DOWNLOADURL:-""}" diff --git a/features/src/github-copilot-cli/installer.go b/features/src/github-copilot-cli/installer.go new file mode 100644 index 0000000..250cf2c --- /dev/null +++ b/features/src/github-copilot-cli/installer.go @@ -0,0 +1,108 @@ +package main + +import ( + "builder/installer" + "flag" + "fmt" + "os" + "path/filepath" + "regexp" + + "github.com/roemer/gover" +) + +////////// +// Configuration +////////// + +var versionRegex *regexp.Regexp = regexp.MustCompile(`(?m)^v(?P(\d+)\.(\d+)\.(\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", "latest", "") + downloadUrl := flag.String("downloadUrl", "", "") + flag.Parse() + + // Load settings from an external file + if err := installer.LoadOverrides(); err != nil { + return err + } + + // Apply override logic for URLs + installer.HandleGitHubOverride(downloadUrl, "github/copilot-cli", "github-copilot-cli-download-url") + + // Create and process the feature + feature := installer.NewFeature("GitHub Copilot CLI", false, + &githubCopilotCliComponent{ + ComponentBase: installer.NewComponentBase("GitHub Copilot CLI", *version), + DownloadUrl: *downloadUrl, + }) + return feature.Process() +} + +////////// +// Implementation +////////// + +type githubCopilotCliComponent struct { + *installer.ComponentBase + DownloadUrl string +} + +func (c *githubCopilotCliComponent) GetAllVersions() ([]*gover.Version, error) { + tags, err := installer.Tools.GitHub.GetTags("github", "copilot-cli") + if err != nil { + return nil, err + } + return installer.Tools.Versioning.ParseVersionsFromList(tags, versionRegex, true) +} + +func (c *githubCopilotCliComponent) InstallVersion(version *gover.Version) error { + archPart, err := installer.Tools.System.MapArchitecture(map[string]string{ + installer.AMD64: "x64", + installer.ARM64: "arm64", + }) + if err != nil { + return err + } + + // Download the archive + fileName := fmt.Sprintf("copilot-linux-%s.tar.gz", archPart) + downloadUrl, err := installer.Tools.Http.BuildUrl(c.DownloadUrl, "v"+version.Raw, fileName) + if err != nil { + return err + } + if err := installer.Tools.Download.ToFile(downloadUrl, fileName, "GitHub Copilot CLI"); err != nil { + return err + } + defer os.Remove(fileName) + + // Extract to a temp directory + tempDir, err := os.MkdirTemp("", "github-copilot-cli-extract") + if err != nil { + return err + } + defer os.RemoveAll(tempDir) + + if err := installer.Tools.Compression.ExtractTarGz(fileName, tempDir, false); err != nil { + return err + } + + // Install the binary + if err := installer.Tools.System.InstallBinaryToUsrLocalBin(filepath.Join(tempDir, "copilot"), "copilot"); err != nil { + return err + } + + return nil +} diff --git a/features/test/github-copilot-cli/install.sh b/features/test/github-copilot-cli/install.sh new file mode 100644 index 0000000..53432db --- /dev/null +++ b/features/test/github-copilot-cli/install.sh @@ -0,0 +1,8 @@ +#!/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_file_exists "/usr/local/bin/copilot" +check_version "$(copilot --version)" "1.0.39" diff --git a/features/test/github-copilot-cli/scenarios.json b/features/test/github-copilot-cli/scenarios.json new file mode 100644 index 0000000..a9fcc22 --- /dev/null +++ b/features/test/github-copilot-cli/scenarios.json @@ -0,0 +1,15 @@ +{ + "install": { + "build": { + "dockerfile": "Dockerfile", + "options": [ + "--add-host=host.docker.internal:host-gateway" + ] + }, + "features": { + "./github-copilot-cli": { + "version": "1.0.39" + } + } + } +} diff --git a/features/test/github-copilot-cli/test-images.json b/features/test/github-copilot-cli/test-images.json new file mode 100644 index 0000000..5c1d5eb --- /dev/null +++ b/features/test/github-copilot-cli/test-images.json @@ -0,0 +1,5 @@ +[ + "mcr.microsoft.com/devcontainers/base:debian-11", + "mcr.microsoft.com/devcontainers/base:debian-12", + "mcr.microsoft.com/devcontainers/base:ubuntu-24.04" +] diff --git a/override-all.env b/override-all.env index b1c2c21..a720bc8 100644 --- a/override-all.env +++ b/override-all.env @@ -30,6 +30,9 @@ DOTNET_NUGET_CONFIG_PATH="" # git-lfs GIT_LFS_DOWNLOAD_URL="" +# github-copilot-cli +GITHUB_COPILOT_CLI_DOWNLOAD_URL="" + # gitlab-cli GITLAB_CLI_DOWNLOAD_URL="" GITLAB_CLI_VERSIONS_URL=""