diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index bc9d869..475b0ad 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -13,8 +13,7 @@ jobs:
strategy:
matrix:
features:
- # - color
- # - hello
+ - dart
baseImage:
- debian:latest
- ubuntu:latest
@@ -34,8 +33,7 @@ jobs:
strategy:
matrix:
features:
- # - color
- # - hello
+ - dart
steps:
- uses: actions/checkout@v3
diff --git a/src/dart/NOTES.md b/src/dart/NOTES.md
new file mode 100644
index 0000000..7608082
--- /dev/null
+++ b/src/dart/NOTES.md
@@ -0,0 +1,45 @@
+
+
+
+
+[](https://dart.dev/)
+
+
+
+> Dart is a client-optimized language for fast apps on any platform
+
+— [Dart programming language | Dart]
+
+This feature installs the Dart SDK.
+
+🆘 If you're having trouble with this feature, [open a Discussion] or [open an
+Issue]! We'd be happy to fix bugs! 🐛
+
+## Supported platforms
+
+`linux/amd64` and `linux/arm64` platforms `debian` and `ubuntu`.
+
+## How to specify extensions?
+
+This feature installs any version with the `version` option and `channel` option.
+Check [Dart SDK archive] for channel details.
+
+```json
+"features": {
+ "ghcr.io/devcontainers-community/features/dart:1": {
+ "version": "3",
+ "channel": "beta"
+ }
+}
+```
+
+Note that the beta and dev channels use suffix-based version matching,
+so only versions ending with channel name (e.g. `3.1.0-63.1.beta`) can be installed.
+
+
+[Dart programming language | Dart]: https://dart.dev/
+[Dart SDK archive]: https://dart.dev/get-dart/archive
+[Flutter]: https://flutter.dev/
+[open a Discussion]: https://github.com/devcontainers-community/features/discussions/new?category=q-a
+[open an Issue]: https://github.com/devcontainers-community/features/issues/new
+
diff --git a/src/dart/_common.sh b/src/dart/_common.sh
new file mode 100644
index 0000000..a87e86a
--- /dev/null
+++ b/src/dart/_common.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+apt_get_update() {
+ if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
+ echo "Running apt-get update..."
+ apt-get update -y
+ fi
+}
+
+# Checks if packages are installed and installs them if not
+check_packages() {
+ if ! dpkg -s "$@" >/dev/null 2>&1; then
+ apt_get_update
+ apt-get -y install --no-install-recommends "$@"
+ fi
+}
+
+check_git() {
+ if [ ! -x "$(command -v git)" ]; then
+ check_packages git
+ fi
+}
diff --git a/src/dart/devcontainer-feature.json b/src/dart/devcontainer-feature.json
new file mode 100644
index 0000000..749a5b0
--- /dev/null
+++ b/src/dart/devcontainer-feature.json
@@ -0,0 +1,28 @@
+{
+ "id": "dart",
+ "name": "Dart",
+ "description": "Installs the Dart SDK",
+ "version": "1.0.0",
+ "options": {
+ "version": {
+ "description": "Select version of Dart SDK",
+ "type": "string",
+ "default": "latest",
+ "proposals": ["latest", "3"]
+ },
+ "channel": {
+ "description": "Select build channel of Dart SDK",
+ "type": "string",
+ "default": "stable",
+ "enum": ["stable", "beta", "dev", "main"]
+ }
+ },
+ "containerEnv": {
+ "PATH": "/usr/lib/dart/bin:${PATH}"
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": ["dart-code.dart-code"]
+ }
+ }
+}
diff --git a/src/dart/install.sh b/src/dart/install.sh
new file mode 100644
index 0000000..11339a6
--- /dev/null
+++ b/src/dart/install.sh
@@ -0,0 +1,113 @@
+#!/usr/bin/env bash
+
+VERSION=${VERSION:-"latest"}
+CHANNEL=${CHANNEL:-"stable"}
+
+DART_SDK="/usr/lib/dart"
+
+set -e
+source _common.sh
+
+if [ "$(id -u)" -ne 0 ]; then
+ echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
+ exit 1
+fi
+
+architecture="$(dpkg --print-architecture)"
+if [ "${architecture}" != "amd64" ] && [ "${architecture}" != "arm64" ]; then
+ echo "(!) Architecture $architecture unsupported"
+ exit 1
+fi
+
+# Clean up
+rm -rf /var/lib/apt/lists/*
+
+find_version_from_git_tags() {
+ local variable_name=$1
+ local requested_version=${!variable_name}
+ if [ "${requested_version}" = "none" ]; then return; fi
+ local repository=$2
+ local prefix=${3:-"tags/v"}
+ local separator=${4:-"."}
+ local last_part_optional=${5:-""}
+ if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then
+ local escaped_separator=${separator//./\\.}
+ local last_part
+ if [ -n "${last_part_optional}" ]; then
+ last_part=".*${last_part_optional}"
+ else
+ last_part=""
+ fi
+ local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${escaped_separator}[0-9]+${last_part}$"
+ local version_list
+ check_git
+ check_packages ca-certificates
+ version_list="$(git ls-remote --tags "${repository}" | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)"
+ if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then
+ declare -g "${variable_name}"="$(echo "${version_list}" | head -n 1)"
+ else
+ set +e
+ declare -g "${variable_name}"="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")"
+ set -e
+ fi
+ fi
+ if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" >/dev/null 2>&1; then
+ echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2
+ exit 1
+ fi
+ echo "${variable_name}=${!variable_name}"
+}
+
+export DEBIAN_FRONTEND=noninteractive
+
+check_packages curl ca-certificates unzip
+
+case "$architecture" in
+amd64)
+ SDK_ARCH="x64"
+ ;;
+arm64)
+ SDK_ARCH="arm64"
+ ;;
+esac
+
+if [ "${CHANNEL}" = "main" ]; then
+ URL="https://storage.googleapis.com/dart-archive/channels/be/raw/latest/sdk/dartsdk-linux-${SDK_ARCH}-release.zip"
+else
+ if [ "${CHANNEL}" = "stable" ]; then
+ LAST_PART=""
+ elif [ "${CHANNEL}" = "beta" ]; then
+ LAST_PART="beta"
+ elif [ "${CHANNEL}" = "dev" ]; then
+ LAST_PART="dev"
+ else
+ echo "(!) Channel ${CHANNEL} unsupported"
+ exit 1
+ fi
+ # Soft version matching
+ find_version_from_git_tags VERSION "https://github.com/dart-lang/sdk" "tags/" "." "${LAST_PART}"
+
+ URL="https://storage.googleapis.com/dart-archive/channels/${CHANNEL}/release/${VERSION}/sdk/dartsdk-linux-${SDK_ARCH}-release.zip"
+fi
+
+echo "Downloading Dart..."
+
+mkdir "${DART_SDK}"
+
+# Working directory
+mkdir /tmp/dvcf-dart
+pushd /tmp/dvcf-dart
+
+curl -sL "${URL}" -o dart.zip
+unzip -q dart.zip
+mv dart-sdk/* "${DART_SDK}"/.
+
+chmod 755 "${DART_SDK}" "${DART_SDK}/bin"
+
+popd
+rm -rf /tmp/dvcf-dart
+
+# Clean up
+rm -rf /var/lib/apt/lists/*
+
+echo "Done!"
diff --git a/test/dart/channel-beta.sh b/test/dart/channel-beta.sh
new file mode 100644
index 0000000..2c16de8
--- /dev/null
+++ b/test/dart/channel-beta.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+set -e
+source dev-container-features-test-lib
+
+LATEST_VERSION="$(git ls-remote --tags https://github.com/dart-lang/sdk | grep -oP "(?<=refs/tags/)[0-9]+\\.[0-9]+\\.[0-9]+.*beta$" | sort -V | tail -n 1)"
+
+check 'dart' bash -c "dart --version | grep ${LATEST_VERSION}"
+
+reportResults
diff --git a/test/dart/channel-main.sh b/test/dart/channel-main.sh
new file mode 100644
index 0000000..c9e76e8
--- /dev/null
+++ b/test/dart/channel-main.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -e
+source dev-container-features-test-lib
+
+check 'dart' bash -c "dart --version"
+
+reportResults
diff --git a/test/dart/scenarios.json b/test/dart/scenarios.json
new file mode 100644
index 0000000..1b2f9e2
--- /dev/null
+++ b/test/dart/scenarios.json
@@ -0,0 +1,26 @@
+{
+ "version-3": {
+ "image": "mcr.microsoft.com/devcontainers/base:debian",
+ "features": {
+ "dart": {
+ "version": "3"
+ }
+ }
+ },
+ "channel-beta": {
+ "image": "mcr.microsoft.com/devcontainers/base:debian",
+ "features": {
+ "dart": {
+ "channel": "beta"
+ }
+ }
+ },
+ "channel-main": {
+ "image": "mcr.microsoft.com/devcontainers/base:debian",
+ "features": {
+ "dart": {
+ "channel": "main"
+ }
+ }
+ }
+}
diff --git a/test/dart/test.sh b/test/dart/test.sh
new file mode 100644
index 0000000..4f4be27
--- /dev/null
+++ b/test/dart/test.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+set -e
+source dev-container-features-test-lib
+
+# Clean up
+rm -rf /var/lib/apt/lists/*
+
+apt_get_update() {
+ if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
+ echo "Running apt-get update..."
+ apt-get update -y
+ fi
+}
+
+check_packages() {
+ if ! dpkg -s "$@" >/dev/null 2>&1; then
+ apt_get_update
+ apt-get -y install --no-install-recommends "$@"
+ fi
+}
+
+check_packages git
+LATEST_VERSION="$(git ls-remote --tags https://github.com/dart-lang/sdk | grep -oP "(?<=refs/tags/)[0-9]+\\.[0-9]+\\.[0-9]+$" | sort -V | tail -n 1)"
+
+check 'dart' bash -c "dart --version | grep ${LATEST_VERSION}"
+
+reportResults
diff --git a/test/dart/version-3.sh b/test/dart/version-3.sh
new file mode 100644
index 0000000..efae003
--- /dev/null
+++ b/test/dart/version-3.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+set -e
+source dev-container-features-test-lib
+
+LATEST_VERSION="$(git ls-remote --tags https://github.com/dart-lang/sdk | grep -oP "(?<=refs/tags/)3\\.[0-9]+\\.[0-9]+$" | sort -V | tail -n 1)"
+
+check 'dart' bash -c "dart --version | grep ${LATEST_VERSION}"
+
+reportResults