diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4033ca9..5224dcb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -13,6 +13,7 @@ jobs: strategy: matrix: features: + - go-mod-cache - color - hello baseImage: @@ -34,6 +35,7 @@ jobs: strategy: matrix: features: + - go-mod-cache - color - hello steps: diff --git a/README.md b/README.md index 1a1cea8..798640b 100644 --- a/README.md +++ b/README.md @@ -1,188 +1,14 @@ -# Dev Container Features: Self Authoring Template +# Dev Container Features -> This repo provides a starting point and example for creating your own custom [dev container Features](https://containers.dev/implementors/features/), hosted for free on GitHub Container Registry. The example in this repository follows the [dev container Feature distribution specification](https://containers.dev/implementors/features-distribution/). -> -> To provide feedback to the specification, please leave a comment [on spec issue #70](https://github.com/devcontainers/spec/issues/70). For more broad feedback regarding dev container Features, please see [spec issue #61](https://github.com/devcontainers/spec/issues/61). +This repo contains a collection of custom [Dev Container Features](https://containers.dev/implementors/features/), hosted for free on GitHub Container Registry. -## Example Contents +## Features -This repository contains a _collection_ of two Features - `hello` and `color`. These Features serve as simple feature implementations. Each sub-section below shows a sample `devcontainer.json` alongside example usage of the Feature. +| Feature | Description | +| --------------- | ----------- | +|[`go-mod-cache`](src/go-mod-cache) | Mounts the Go Module cache (`GOMODCACHE`) to a Docker Volume to share between multiple Dev Containers. | -### `hello` -Running `hello` inside the built container will print the greeting provided to it via its `greeting` option. +--- -```jsonc -{ - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - "features": { - "ghcr.io/devcontainers/feature-starter/hello:1": { - "greeting": "Hello" - } - } -} -``` - -```bash -$ hello - -Hello, user. -``` - -### `color` - -Running `color` inside the built container will print your favorite color to standard out. - -```jsonc -{ - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - "features": { - "ghcr.io/devcontainers/feature-starter/color:1": { - "favorite": "green" - } - } -} -``` - -```bash -$ color - -my favorite color is green -``` - -## Repo and Feature Structure - -Similar to the [`devcontainers/features`](https://github.com/devcontainers/features) repo, this repository has a `src` folder. Each Feature has its own sub-folder, containing at least a `devcontainer-feature.json` and an entrypoint script `install.sh`. - -``` -├── src -│ ├── hello -│ │ ├── devcontainer-feature.json -│ │ └── install.sh -│ ├── color -│ │ ├── devcontainer-feature.json -│ │ └── install.sh -| ├── ... -│ │ ├── devcontainer-feature.json -│ │ └── install.sh -... -``` - -An [implementing tool](https://containers.dev/supporting#tools) will composite [the documented dev container properties](https://containers.dev/implementors/features/#devcontainer-feature-json-properties) from the feature's `devcontainer-feature.json` file, and execute in the `install.sh` entrypoint script in the container during build time. Implementing tools are also free to process attributes under the `customizations` property as desired. - -### Options - -All available options for a Feature should be declared in the `devcontainer-feature.json`. The syntax for the `options` property can be found in the [devcontainer Feature json properties reference](https://containers.dev/implementors/features/#devcontainer-feature-json-properties). - -For example, the `color` feature provides an enum of three possible options (`red`, `gold`, `green`). If no option is provided in a user's `devcontainer.json`, the value is set to "red". - -```jsonc -{ - // ... - "options": { - "favorite": { - "type": "string", - "enum": [ - "red", - "gold", - "green" - ], - "default": "red", - "description": "Choose your favorite color." - } - } -} -``` - -Options are exported as Feature-scoped environment variables. The option name is captialized and sanitized according to [option resolution](https://containers.dev/implementors/features/#option-resolution). - -```bash -#!/bin/bash - -echo "Activating feature 'color'" -echo "The provided favorite color is: ${FAVORITE}" - -... -``` - -## Distributing Features - -### Versioning - -Features are individually versioned by the `version` attribute in a Feature's `devcontainer-feature.json`. Features are versioned according to the semver specification. More details can be found in [the dev container Feature specification](https://containers.dev/implementors/features/#versioning). - -### Publishing - -> NOTE: The Distribution spec can be [found here](https://containers.dev/implementors/features-distribution/). -> -> While any registry [implementing the OCI Distribution spec](https://github.com/opencontainers/distribution-spec) can be used, this template will leverage GHCR (GitHub Container Registry) as the backing registry. - -Features are meant to be easily sharable units of dev container configuration and installation code. - -This repo contains a **GitHub Action** [workflow](.github/workflows/release.yaml) that will publish each Feature to GHCR. - -*Allow GitHub Actions to create and approve pull requests* should be enabled in the repository's `Settings > Actions > General > Workflow permissions` for auto generation of `src//README.md` per Feature (which merges any existing `src//NOTES.md`). - -By default, each Feature will be prefixed with the `` namespace. For example, the two Features in this repository can be referenced in a `devcontainer.json` with: - -``` -ghcr.io/devcontainers/feature-starter/color:1 -ghcr.io/devcontainers/feature-starter/hello:1 -``` - -The provided GitHub Action will also publish a third "metadata" package with just the namespace, eg: `ghcr.io/devcontainers/feature-starter`. This contains information useful for tools aiding in Feature discovery. - -'`devcontainers/feature-starter`' is known as the feature collection namespace. - -### Marking Feature Public - -Note that by default, GHCR packages are marked as `private`. To stay within the free tier, Features need to be marked as `public`. - -This can be done by navigating to the Feature's "package settings" page in GHCR, and setting the visibility to 'public`. The URL may look something like: - -``` -https://github.com/users//packages/container/%2F/settings -``` - -image - -### Adding Features to the Index - -If you'd like your Features to appear in our [public index](https://containers.dev/features) so that other community members can find them, you can do the following: - -* Go to [github.com/devcontainers/devcontainers.github.io](https://github.com/devcontainers/devcontainers.github.io) - * This is the GitHub repo backing the [containers.dev](https://containers.dev/) spec site -* Open a PR to modify the [collection-index.yml](https://github.com/devcontainers/devcontainers.github.io/blob/gh-pages/_data/collection-index.yml) file - -This index is from where [supporting tools](https://containers.dev/supporting) like [VS Code Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) and [GitHub Codespaces](https://github.com/features/codespaces) surface Features for their dev container creation UI. - -#### Using private Features in Codespaces - -For any Features hosted in GHCR that are kept private, the `GITHUB_TOKEN` access token in your environment will need to have `package:read` and `contents:read` for the associated repository. - -Many implementing tools use a broadly scoped access token and will work automatically. GitHub Codespaces uses repo-scoped tokens, and therefore you'll need to add the permissions in `devcontainer.json` - -An example `devcontainer.json` can be found below. - -```jsonc -{ - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - "features": { - "ghcr.io/my-org/private-features/hello:1": { - "greeting": "Hello" - } - }, - "customizations": { - "codespaces": { - "repositories": { - "my-org/private-features": { - "permissions": { - "packages": "read", - "contents": "read" - } - } - } - } - } -} -``` +Made with ✨ & ❤️ by [ForWarD Software](https://github.com/forwardsoftware) and [contributors](https://github.com/forwardsoftware/devcontainer-features/graphs/contributors) diff --git a/src/go-mod-cache/README.md b/src/go-mod-cache/README.md new file mode 100644 index 0000000..d2350f6 --- /dev/null +++ b/src/go-mod-cache/README.md @@ -0,0 +1,25 @@ + +# Go Mod Cache (go-mod-cache) + +Mounts the Go Module cache (`GOMODCACHE`) to a Docker Volume to share between multiple Dev Containers. + +## Example Usage + +```json +"features": { + "ghcr.io/forwardsoftware/devcontainer-features/go-mod-cache:1": { + } +} +``` + +## Options + +| Options Id | Description | Type | Default Value | +|-----|-----|-----|-----| + + + + +--- + +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/feature-starter/blob/main/src/hello/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/go-mod-cache/devcontainer-feature.json b/src/go-mod-cache/devcontainer-feature.json new file mode 100644 index 0000000..5a5eb17 --- /dev/null +++ b/src/go-mod-cache/devcontainer-feature.json @@ -0,0 +1,18 @@ +{ + "name": "Go Mod Cache", + "id": "go-mod-cache", + "version": "1.0.0", + "description": "Mounts the Go Module cache (`GOMODCACHE`) to a Docker Volume to share between multiple Dev Containers.", + "options": {}, + "mounts": [ + { + "source": "global-devcontainer-go-cache", + "target": "/mnt/go-cache", + "type": "volume" + } + ], + "installsAfter": [ + "ghcr.io/devcontainers/features/common-utils", + "ghcr.io/devcontainers/features/go" + ] +} \ No newline at end of file diff --git a/src/go-mod-cache/install.sh b/src/go-mod-cache/install.sh new file mode 100644 index 0000000..c3db5cf --- /dev/null +++ b/src/go-mod-cache/install.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +set -e + +echo "Activating feature 'go-mod-cache'" + +USERNAME=${USERNAME:-${_REMOTE_USER}} +GOPATH=${GOPATH:-/go} + +setup_groups() { + if ! getent group golang > /dev/null; then + echo "Group 'golang' does not exist. Creating..." + groupadd golang + else + echo "Group 'golang' already exists. Skip creation..." + fi +} + +create_dir() { + local target_dir=$1 + local username=$2 + + if [ -d "$target_dir" ]; then + echo "Directory $target_dir already exists. Skip creation..." + else + echo "Create directory $target_dir..." + mkdir -p "$target_dir" + fi + + if [ -z "$username" ]; then + echo "No username provided. Skip chown..." + else + echo "Change owner of $target_dir to $username..." + chown -R "$username:golang" "$target_dir" + fi +} + +symlink_dirs() { + local local_dir=$1 + local cache_dir=$2 + local username=$3 + + # if the folder we want to symlink already exists, the ln -s command will create a folder inside the existing folder + if [ -e "$local_dir" ]; then + echo "Moving existing $local_dir folder to $local_dir-old" + mv "$local_dir" "$local_dir-old" + fi + + echo "Symlink $local_dir to $cache_dir for $username..." + runuser -u "$username" -- ln -s "$cache_dir" "$local_dir" +} + +setup_groups +create_dir "/mnt/go-cache" "${USERNAME}" +create_dir "$GOPATH" "${USERNAME}" +symlink_dirs "$GOPATH/pkg" "/mnt/go-cache" "${USERNAME}" + +echo "Finished installing 'go-mod-cache'" \ No newline at end of file diff --git a/test/go-mod-cache/custom_user.sh b/test/go-mod-cache/custom_user.sh new file mode 100644 index 0000000..9b2c250 --- /dev/null +++ b/test/go-mod-cache/custom_user.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +# tests installing feature with a custom user + +./test.sh \ No newline at end of file diff --git a/test/go-mod-cache/go_feature.sh b/test/go-mod-cache/go_feature.sh new file mode 100644 index 0000000..e1f3d83 --- /dev/null +++ b/test/go-mod-cache/go_feature.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +# tests installing feature when go is installed as feature + +./test.sh diff --git a/test/go-mod-cache/scenarios.json b/test/go-mod-cache/scenarios.json new file mode 100644 index 0000000..d2b5c22 --- /dev/null +++ b/test/go-mod-cache/scenarios.json @@ -0,0 +1,35 @@ +{ + "with_go": { + "image": "mcr.microsoft.com/devcontainers/go:1-1", + "features": { + "go-mod-cache": {} + } + }, + "go_feature": { + "image": "mcr.microsoft.com/devcontainers/base", + "features": { + "ghcr.io/devcontainers/features/go:1": {}, + "go-mod-cache": {} + } + }, + "zsh_shell": { + "image": "mcr.microsoft.com/devcontainers/go:1-1", + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "configureZshAsDefaultShell": true + }, + "go-mod-cache": {} + } + }, + "custom_user": { + "image": "mcr.microsoft.com/devcontainers/base", + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "username": "golang" + }, + "ghcr.io/devcontainers/features/go:1": {}, + "go-mod-cache": {} + }, + "remoteUser": "golang" + } +} \ No newline at end of file diff --git a/test/go-mod-cache/test.sh b/test/go-mod-cache/test.sh new file mode 100644 index 0000000..a70eb03 --- /dev/null +++ b/test/go-mod-cache/test.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# This test file will be executed against an auto-generated devcontainer.json that +# includes the 'hello' Feature with no options. +# +# For more information, see: https://github.com/devcontainers/cli/blob/main/docs/features/test.md +# +# Eg: +# { +# "image": "<..some-base-image...>", +# "features": { +# "hello": {} +# }, +# "remoteUser": "root" +# } +# +# Thus, the value of all options will fall back to the default value in +# the Feature's 'devcontainer-feature.json'. +# For the 'hello' feature, that means the default favorite greeting is 'hey'. +# +# These scripts are run as 'root' by default. Although that can be changed +# with the '--remote-user' flag. +# +# This test can be run with the following command: +# +# devcontainer features test \ +# --features hello \ +# --remote-user root \ +# --skip-scenarios \ +# --base-image mcr.microsoft.com/devcontainers/base:ubuntu \ +# /path/to/this/repo + +set -e + +# Optional: Import test library bundled with the devcontainer CLI +# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib +# Provides the 'check' and 'reportResults' commands. +source dev-container-features-test-lib + +# Feature-specific tests +# The 'check' command comes from the dev-container-features-test-lib. Syntax is... +# check