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
10 changes: 9 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
{
"name": "PF dev-feat DEV",
"dockerFile": "Dockerfile",
"build": {
"dockerfile": "Dockerfile",
"options": [
"--add-host=host.docker.internal:host-gateway"
]
},
"runArgs": [
"--add-host=host.docker.internal:host-gateway"
],
"containerEnv": {
"GOMODCACHE": "${containerWorkspaceFolder}/.go_cache"
},
Expand Down
4 changes: 1 addition & 3 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@
- **Build**: Use Go to build/test feature logic. No monolithic build; each feature is self-contained.
- **Testing**: Test scripts are in `features/test/<feature>/`. Use shell scripts for scenario-based testing.
- **Overrides**: Set `DEV_FEATURE_OVERRIDE_LOCATION` to a file or URL for global download URL overrides. See `override-all.env` for supported keys.
- **Version Resolution**: Features support both explicit and partial versioning. Set `versionResolve: true` to enable partial version resolution.
- **Version Resolution**: Features support both explicit and partial versioning.

## Project Conventions
- Prefer explicit version pinning for reproducibility; partial versions require `versionResolve: true`.
- Download URLs are split into `downloadUrlBase` and `downloadUrlPath` for flexibility with proxies/caches.
- All features support ARM64 and AMD64 where possible; Alpine support is best-effort.
- Use Go for core installer logic, with shell scripts for entrypoints.

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
run: go run ./build --target "Feature:${{ matrix.feature }}:Test"

- name: Publish
if: github.ref == 'refs/heads/main'
run: go run ./build --target "Feature:${{ matrix.feature }}:Publish"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 changes: 9 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ Below is a list with included features, click on the link for more details.

Most features allow you to define one or more versions of the software that should be installed by the feature.

The preferred way to do this is by defining the full version number, e.g. 1.24.3 for Go. The features by default try to directly download this version and fail if this does not work.
The logic in the feature to decide which version to install is as follows:
- The passed version number is a full version (e.g. 1.24.3) -> Directly install it and fail if it does not work
- The passed version number is a partial version (e.g. 1.24) -> Lookup the versions and find the highest one which still matches the given one
- The passed version number is `latest` -> Lookup the versions and find the highest one.

Alternatively, the version often can be set to `latest` or a partial version (e.g. `1.24`). In this case, the feature will resolve the version and find out the appropriate one to install and install that.
The preferred way to do this is always by defining the full version number.

NOTE: If you use partial versions, there is also a matching `versionResolve` option that needs to be set to `true` in order for the resolving to be used. This is because some tools do not respect semver and deploy eg. version 2.13.0 as 2.13 instead of 2.13.0, so with this flag, we can distinguish those cases.

NOTE2: The URLs for resolving the versions are usually in separate configuration options. This is so that a feature can be configured to use a cache service (e.g. Artifactory) for downloading the binaries but use the `live` URL for checking versions.
NOTE: The URLs for resolving the versions are usually in separate configuration options. This is so that a feature can be configured to use a cache service (e.g. Artifactory) for downloading the binaries but use the `live` URL for checking versions.

### Global overrides

Expand All @@ -51,20 +52,9 @@ An environment variable `DEV_FEATURE_OVERRIDE_LOCATION` can be set to a location
The content of the file is simple `key=value` like an env file.
The key names are `DEV_FEATURE_OVERRIDE_<key-to-override>`, but you can also just skip the `DEV_FEATURE_OVERRIDE_` and directly use the desired key name. So for example:
```
GO_DOWNLOAD_URL_BASE=https://mycompany.com/artifactory/dl-google-generic-remote
GO_DOWNLOAD_URL_PATH=/go
GO_DOWNLOAD_URL=https://mycompany.com/artifactory/dl-google-generic-remote/go
```
As the remote can be configured differently (e.g. by including or excluding sub-paths), there are usually two variables: one for the base and one for the path.

Example:

If your remote points to `https://dl.google.com`, you need to set:
* base = "your-remote"
* url = "/go" (the default) or leave out the URL, then it will use the default

If your remote points directly to `https://dl.google.com/go`, you need to set:
* base = "your-remote"
* url = "/" (so it will not add any sub-path after the remote)
As the remote can be configured differently (e.g. by including or excluding sub-paths), you might need to check and see which part of the path is expected to be included. Usually as much as possible.

See [override-all.env](./override-all.env) for a file with all possible override variables.

Expand All @@ -73,7 +63,7 @@ See [override-all.env](./override-all.env) for a file with all possible override
There are a few sources which are used in multiple installations. For those sources, there is an override that globaly overrides all installations from this sources. Here is the list of those sources and their keys.

```
GITHUB_DOWNLOAD_URL_BASE=...
DEV_FEATURE_OVERRIDE_GITHUB_DOWNLOAD_URL=...
```

### Extend an existing feature
Expand Down
4 changes: 4 additions & 0 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ func prepareFeature(featureName string, targetDir string) error {
if err := os.CopyFS(targetDir, os.DirFS(featurePath)); err != nil {
return err
}
// Copy the functions file
if err := installer.Tools.FileSystem.CopyFile("features/src/functions.sh", filepath.Join(targetDir, "functions.sh")); err != nil {
return err
}
// Build the installer
_, err := buildGo(targetDir, "installer")
if err != nil {
Expand Down
26 changes: 7 additions & 19 deletions features/src/docker-out/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,14 @@ A feature which installs the Docker client and re-uses the host socket.

```json
"features": {
"ghcr.io/postfinance/devcontainer-features/docker-out:0.1.0": {
"ghcr.io/postfinance/devcontainer-features/docker-out:0.2.0": {
"version": "latest",
"versionResolve": false,
"composeVersion": "latest",
"composeVersionResolve": false,
"buildxVersion": "latest",
"buildxVersionResolve": false,
"downloadUrlBase": "",
"downloadUrlPath": "",
"downloadUrl": "",
"versionsUrl": "",
"composeDownloadUrlBase": "",
"composeDownloadUrlPath": "",
"buildxDownloadUrlBase": "",
"buildxDownloadUrlPath": ""
"composeDownloadUrl": "",
"buildxDownloadUrl": ""
}
}
```
Expand All @@ -29,18 +23,12 @@ A feature which installs the Docker client and re-uses the host socket.
| Option | Description | Type | Default Value | Proposals |
|-----|-----|-----|-----|-----|
| version | The version of the Docker CLI to install. | string | latest | latest, 28.3.3, 20.10 |
| versionResolve | Whether to resolve the version automatically. | boolean | false | true, false |
| composeVersion | The version of the Compose plugin to install. | string | latest | latest, none, 2.39.1, 2.29 |
| composeVersionResolve | Whether to resolve the version automatically. | boolean | false | true, false |
| buildxVersion | The version of the buildx plugin to install. | string | latest | latest, none, 0.26.1, 0.10 |
| buildxVersionResolve | Whether to resolve the version automatically. | boolean | false | true, false |
| downloadUrlBase | The download URL to use for Docker binaries. | string | &lt;empty&gt; | |
| downloadUrlPath | The download URL path to use for Docker binaries. | string | &lt;empty&gt; | |
| downloadUrl | The download URL to use for Docker binaries. | string | &lt;empty&gt; | |
| versionsUrl | The URL to use for checking available versions. | string | &lt;empty&gt; | |
| composeDownloadUrlBase | The download URL to use for Docker Compose binaries. | string | &lt;empty&gt; | |
| composeDownloadUrlPath | The download URL path to use for Docker Compose binaries. | string | &lt;empty&gt; | |
| buildxDownloadUrlBase | The download URL to use for Docker Buildx binaries. | string | &lt;empty&gt; | |
| buildxDownloadUrlPath | The download URL path to use for Docker Buildx binaries. | string | &lt;empty&gt; | |
| composeDownloadUrl | The download URL to use for Docker Compose binaries. | string | &lt;empty&gt; | |
| buildxDownloadUrl | The download URL to use for Docker Buildx binaries. | string | &lt;empty&gt; | |

## Customizations

Expand Down
38 changes: 4 additions & 34 deletions features/src/docker-out/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "docker-out",
"version": "0.1.0",
"version": "0.2.0",
"name": "Docker outside Docker",
"description": "A feature which installs the Docker client and re-uses the host socket.",
"options": {
Expand All @@ -14,11 +14,6 @@
"default": "latest",
"description": "The version of the Docker CLI to install."
},
"versionResolve": {
"type": "boolean",
"default": false,
"description": "Whether to resolve the version automatically."
},
"composeVersion": {
"type": "string",
"proposals": [
Expand All @@ -30,11 +25,6 @@
"default": "latest",
"description": "The version of the Compose plugin to install."
},
"composeVersionResolve": {
"type": "boolean",
"default": false,
"description": "Whether to resolve the version automatically."
},
"buildxVersion": {
"type": "string",
"proposals": [
Expand All @@ -46,45 +36,25 @@
"default": "latest",
"description": "The version of the buildx plugin to install."
},
"buildxVersionResolve": {
"type": "boolean",
"default": false,
"description": "Whether to resolve the version automatically."
},
"downloadUrlBase": {
"downloadUrl": {
"type": "string",
"default": "",
"description": "The download URL to use for Docker binaries."
},
"downloadUrlPath": {
"type": "string",
"default": "",
"description": "The download URL path to use for Docker binaries."
},
"versionsUrl":{
"type": "string",
"default": "",
"description": "The URL to use for checking available versions."
},
"composeDownloadUrlBase": {
"composeDownloadUrl": {
"type": "string",
"default": "",
"description": "The download URL to use for Docker Compose binaries."
},
"composeDownloadUrlPath": {
"type": "string",
"default": "",
"description": "The download URL path to use for Docker Compose binaries."
},
"buildxDownloadUrlBase": {
"buildxDownloadUrl": {
"type": "string",
"default": "",
"description": "The download URL to use for Docker Buildx binaries."
},
"buildxDownloadUrlPath": {
"type": "string",
"default": "",
"description": "The download URL path to use for Docker Buildx binaries."
}
},
"customizations": {
Expand Down
20 changes: 5 additions & 15 deletions features/src/docker-out/install.sh
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
case $(uname -m | tr '[:upper:]' '[:lower:]') in
x86_64*) ARCH=amd64 ;;
arm*|aarch64*) ARCH=arm64 ;;
*) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;;
esac
. ./functions.sh

"./installer_$ARCH" \
"./installer_$(detect_arch)" \
-version="${VERSION:-"latest"}" \
-versionResolve="${VERSIONRESOLVE:-"false"}" \
-composeVersion="${COMPOSEVERSION:-"latest"}" \
-composeVersionResolve="${COMPOSEVERSIONRESOLVE:-"false"}" \
-buildxVersion="${BUILDXVERSION:-"latest"}" \
-buildxVersionResolve="${BUILDXVERSIONRESOLVE:-"false"}" \
-downloadUrlBase="${DOWNLOADURLBASE:-""}" \
-downloadUrlPath="${DOWNLOADURLPATH:-""}" \
-downloadUrl="${DOWNLOADURL:-""}" \
-versionsUrl="${VERSIONSURL:-""}" \
-composeDownloadUrlBase="${COMPOSEDOWNLOADURLBASE:-""}" \
-composeDownloadUrlPath="${COMPOSEDOWNLOADURLPATH:-""}" \
-buildxDownloadUrlBase="${BUILDXDOWNLOADURLBASE:-""}" \
-buildxDownloadUrlPath="${BUILDXDOWNLOADURLPATH:-""}"
-composeDownloadUrl="${COMPOSEDOWNLOADURL:-""}" \
-buildxDownloadUrl="${BUILDXDOWNLOADURL:-""}"
53 changes: 20 additions & 33 deletions features/src/docker-out/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,48 +34,38 @@ func main() {
func runMain() error {
// Handle the flags
version := flag.String("version", "latest", "")
versionResolve := flag.Bool("versionResolve", false, "")
composeVersion := flag.String("composeVersion", "latest", "")
composeVersionResolve := flag.Bool("composeVersionResolve", false, "")
buildxVersion := flag.String("buildxVersion", "latest", "")
buildxVersionResolve := flag.Bool("buildxVersionResolve", false, "")
downloadUrlBase := flag.String("downloadUrlBase", "", "")
downloadUrlPath := flag.String("downloadUrlPath", "", "")
downloadUrl := flag.String("downloadUrl", "", "")
versionsUrl := flag.String("versionsUrl", "", "")
composeDownloadUrlBase := flag.String("composeDownloadUrlBase", "", "")
composeDownloadUrlPath := flag.String("composeDownloadUrlPath", "", "")
buildxDownloadUrlBase := flag.String("buildxDownloadUrlBase", "", "")
buildxDownloadUrlPath := flag.String("buildxDownloadUrlPath", "", "")
composeDownloadUrl := flag.String("composeDownloadUrl", "", "")
buildxDownloadUrl := flag.String("buildxDownloadUrl", "", "")
flag.Parse()

// Load settings from an external file
if err := installer.LoadOverrides(); err != nil {
return err
}

installer.HandleOverride(downloadUrlBase, "https://download.docker.com", "docker-out-download-url-base")
installer.HandleOverride(downloadUrlPath, "/linux/static/stable", "docker-out-download-url-path")
installer.HandleOverride(downloadUrl, "https://download.docker.com/linux/static/stable", "docker-out-download-url")
installer.HandleOverride(versionsUrl, "https://download.docker.com/linux/static/stable", "docker-out-versions-url")
installer.HandleGitHubOverride(composeDownloadUrlBase, composeDownloadUrlPath, "docker/compose", "docker-out-compose-download-url")
installer.HandleGitHubOverride(buildxDownloadUrlBase, buildxDownloadUrlPath, "docker/buildx", "docker-out-buildx-download-url")
installer.HandleGitHubOverride(composeDownloadUrl, "docker/compose", "docker-out-compose-download-url")
installer.HandleGitHubOverride(buildxDownloadUrl, "docker/buildx", "docker-out-buildx-download-url")

// Create and process the feature
feature := installer.NewFeature("Docker-Out", false,
&dockerCliComponent{
ComponentBase: installer.NewComponentBase("Docker CLI", *version, *versionResolve),
DownloadUrlBase: *downloadUrlBase,
DownloadUrlPath: *downloadUrlPath,
VersionsUrl: *versionsUrl,
ComponentBase: installer.NewComponentBase("Docker CLI", *version),
DownloadUrl: *downloadUrl,
VersionsUrl: *versionsUrl,
},
&dockerComposeComponent{
ComponentBase: installer.NewComponentBase("Docker Compose", *composeVersion, *composeVersionResolve),
DownloadUrlBase: *composeDownloadUrlBase,
DownloadUrlPath: *composeDownloadUrlPath,
ComponentBase: installer.NewComponentBase("Docker Compose", *composeVersion),
DownloadUrl: *composeDownloadUrl,
},
&dockerBuildxComponent{
ComponentBase: installer.NewComponentBase("Docker buildx", *buildxVersion, *buildxVersionResolve),
DownloadUrlBase: *buildxDownloadUrlBase,
DownloadUrlPath: *buildxDownloadUrlPath,
ComponentBase: installer.NewComponentBase("Docker buildx", *buildxVersion),
DownloadUrl: *buildxDownloadUrl,
},
)
return feature.Process()
Expand All @@ -89,9 +79,8 @@ func runMain() error {

type dockerCliComponent struct {
*installer.ComponentBase
DownloadUrlBase string
DownloadUrlPath string
VersionsUrl string
DownloadUrl string
VersionsUrl string
}

func (c *dockerCliComponent) GetAllVersions() ([]*gover.Version, error) {
Expand Down Expand Up @@ -121,7 +110,7 @@ func (c *dockerCliComponent) InstallVersion(version *gover.Version) error {
return err
}
fileName := fmt.Sprintf("docker-%s.tgz", version.Raw)
downloadUrl, err := installer.Tools.Http.BuildUrl(c.DownloadUrlBase, c.DownloadUrlPath, architecturePathPart, fileName)
downloadUrl, err := installer.Tools.Http.BuildUrl(c.DownloadUrl, architecturePathPart, fileName)
if err != nil {
return err
}
Expand Down Expand Up @@ -162,8 +151,7 @@ func (c *dockerCliComponent) getArchitecturePathPart() (string, error) {

type dockerComposeComponent struct {
*installer.ComponentBase
DownloadUrlBase string
DownloadUrlPath string
DownloadUrl string
}

func (c *dockerComposeComponent) GetAllVersions() ([]*gover.Version, error) {
Expand All @@ -185,7 +173,7 @@ func (c *dockerComposeComponent) InstallVersion(version *gover.Version) error {
}
// https://github.com/docker/compose/releases/download/v2.39.2/docker-compose-linux-x86_64
versionPart := fmt.Sprintf("v%s", version.Raw)
downloadUrl, err := installer.Tools.Http.BuildUrl(c.DownloadUrlBase, c.DownloadUrlPath, versionPart, fmt.Sprintf("docker-compose-linux-%s", archPart))
downloadUrl, err := installer.Tools.Http.BuildUrl(c.DownloadUrl, versionPart, fmt.Sprintf("docker-compose-linux-%s", archPart))
if err != nil {
return err
}
Expand All @@ -203,8 +191,7 @@ func (c *dockerComposeComponent) InstallVersion(version *gover.Version) error {

type dockerBuildxComponent struct {
*installer.ComponentBase
DownloadUrlBase string
DownloadUrlPath string
DownloadUrl string
}

func (c *dockerBuildxComponent) GetAllVersions() ([]*gover.Version, error) {
Expand All @@ -226,7 +213,7 @@ func (c *dockerBuildxComponent) InstallVersion(version *gover.Version) error {
}
// https://github.com/docker/buildx/releases/download/v0.26.1/buildx-v0.26.1.linux-amd64
versionPart := fmt.Sprintf("v%s", version.Raw)
downloadUrl, err := installer.Tools.Http.BuildUrl(c.DownloadUrlBase, c.DownloadUrlPath, versionPart, fmt.Sprintf("buildx-%s.linux-%s", versionPart, archPart))
downloadUrl, err := installer.Tools.Http.BuildUrl(c.DownloadUrl, versionPart, fmt.Sprintf("buildx-%s.linux-%s", versionPart, archPart))
if err != nil {
return err
}
Expand Down
7 changes: 7 additions & 0 deletions features/src/functions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
detect_arch() {
case $(uname -m | tr '[:upper:]' '[:lower:]') in
x86_64*) echo "amd64" ;;
arm*|aarch64*) echo "arm64" ;;
*) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;;
esac
}
Loading