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
21 changes: 21 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ jobs:
- name: "Generating tests for '${{ matrix.features }}' against '${{ matrix.baseImage }}'"
run: devcontainer features test -f ${{ matrix.features }} -i ${{ matrix.baseImage }} .

test-native-cli:
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
runner:
- ubuntu-latest
- ubuntu-24.04-arm
feature:
- cf-cli
- credhub-cli
- fly-cli
steps:
- uses: actions/checkout@v3

- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli

- name: "Test native CLI scenarios"
run: devcontainer features test -f ${{ matrix.feature }} --skip-autogenerated --skip-duplicated .

test-global:
runs-on: ubuntu-latest
continue-on-error: true
Expand Down
8 changes: 8 additions & 0 deletions src/cf-cli/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## Architectures

AMD64 uses the Cloud Foundry package download endpoint. ARM64 uses the native
Linux ARM64 archive from the selected GitHub release, so the selected version
must publish that archive. The installer verifies the CLI runs after installation.

`latest`, `current`, and `lts` resolve the latest published GitHub release rather
than the highest Git tag, because tags can exist before their release binaries.
2 changes: 1 addition & 1 deletion src/cf-cli/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "Cloud Foundry CLI",
"id": "cf-cli",
"version": "1.0.0",
"version": "1.0.1",
"description": "Installs the Cloud Foundry CLI.",
"options": {
"version": {
Expand Down
29 changes: 24 additions & 5 deletions src/cf-cli/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

USERNAME=${USERNAME:-${_REMOTE_USER:-"automatic"}}

set -e
set -eo pipefail

export TMPDIR=$(mktemp -d /tmp/feature.XXXXXX)
trap "rm -rf $TMPDIR" EXIT
Expand Down Expand Up @@ -101,13 +101,32 @@ export DEBIAN_FRONTEND=noninteractive

### BEGIN install

# Soft version matching
find_version_from_git_tags VERSION "https://github.com/cloudfoundry/cli"

check_packages curl ca-certificates
# Tags can precede release assets. Default installs must select a published release.
case "${VERSION}" in
latest|current|lts)
release_url="$(curl -fsSL --retry 3 -o /dev/null -w '%{url_effective}' https://github.com/cloudfoundry/cli/releases/latest)"
VERSION="${release_url##*/}"
VERSION="${VERSION#v}"
if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Could not resolve a stable CF CLI release from ${release_url}" >&2
exit 1
fi
echo "VERSION=${VERSION}"
;;
*)
find_version_from_git_tags VERSION "https://github.com/cloudfoundry/cli"
;;
esac
echo "Downloading cf..."
curl -SL "https://packages.cloudfoundry.org/stable?release=linux64-binary&version=${VERSION}" | tar -zxC "$TMPDIR"
if [ "${architecture}" = "arm64" ]; then
download_url="https://github.com/cloudfoundry/cli/releases/download/v${VERSION}/cf${VERSION%%.*}-cli_${VERSION}_linux_arm64.tgz"
else
download_url="https://packages.cloudfoundry.org/stable?release=linux64-binary&version=${VERSION}"
fi
curl -fsSL --retry 3 "${download_url}" | tar -zxC "$TMPDIR"
install "$TMPDIR/cf" /usr/local/bin/cf
cf version

### END install

Expand Down
23 changes: 23 additions & 0 deletions src/credhub-cli/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## Native ARM64 installation

The feature downloads a release archive matching the container architecture.
When a release has no Linux ARM64 archive, it builds the selected source tag with
vendored dependencies instead. Unqualified legacy Linux archives are used only
on AMD64.

For ARM64 source builds, include the official Go feature. Select a Go version
that satisfies the selected CredHub tag's `go.mod`; CredHub 2.9.62 requires Go
1.26.0 or newer. The feature orders itself after Go and uses the installed
toolchain without automatically downloading another version.

```json
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.27.2",
"installGoTools": false
},
"ghcr.io/nulldriver/devcontainer-features/credhub-cli:1": {
"version": "2.9.62"
}
}
```
5 changes: 3 additions & 2 deletions src/credhub-cli/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "CredHub CLI",
"id": "credhub-cli",
"version": "1.0.2",
"version": "1.0.3",
"description": "Installs the CredHub CLI.",
"options": {
"version": {
Expand All @@ -16,6 +16,7 @@
}
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
"ghcr.io/devcontainers/features/common-utils",
"ghcr.io/devcontainers/features/go"
]
}
40 changes: 32 additions & 8 deletions src/credhub-cli/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -102,21 +102,45 @@ export DEBIAN_FRONTEND=noninteractive
### BEGIN install

REPO="cloudfoundry/credhub-cli"
ARCH="amd64"

# Soft version matching
find_version_from_git_tags VERSION "https://github.com/$REPO" "tags/"

check_packages curl ca-certificates
echo "Downloading credhub..."

# Try to download the release with the architecture type
if curl -sL -o /dev/null -w "%{http_code}" "https://github.com/$REPO/releases/download/$VERSION/credhub-linux-$ARCH-$VERSION.tgz" | grep -q "200"; then
curl -sL "https://github.com/$REPO/releases/download/$VERSION/credhub-linux-$ARCH-$VERSION.tgz" | tar -zxC "$TMPDIR"
else
# Fall back to the older naming convention
curl -sL "https://github.com/$REPO/releases/download/$VERSION/credhub-linux-$VERSION.tgz" | tar -zxC "$TMPDIR"
fi
archive="${TMPDIR}/credhub.tgz"
download_url="https://github.com/${REPO}/releases/download/${VERSION}/credhub-linux-${architecture}-${VERSION}.tgz"
http_status="$(curl -sSL --retry 3 -o "${archive}" -w '%{http_code}' "${download_url}")"

case "${http_status}" in
200)
tar -xzf "${archive}" -C "${TMPDIR}"
;;
404)
if [ "${architecture}" = "amd64" ]; then
# Older unqualified Linux archives contain AMD64 binaries only.
curl -fsSL --retry 3 "https://github.com/${REPO}/releases/download/${VERSION}/credhub-linux-${VERSION}.tgz" | tar -zxC "${TMPDIR}"
else
if ! command -v go >/dev/null 2>&1; then
echo "ARM64 source builds require Go. Add ghcr.io/devcontainers/features/go:1 to your devcontainer features." >&2
exit 1
fi
echo "No ARM64 release archive; building CredHub ${VERSION} from its source tag."
mkdir -p "${TMPDIR}/source"
curl -fsSL --retry 3 "https://github.com/${REPO}/archive/refs/tags/${VERSION}.tar.gz" | tar -xz --strip-components=1 -C "${TMPDIR}/source"
(
cd "${TMPDIR}/source"
CGO_ENABLED=0 GOTOOLCHAIN=local GOOS=linux GOARCH="${architecture}" \
go build -mod=vendor -ldflags "-X code.cloudfoundry.org/credhub-cli/version.Version=${VERSION}" -o "${TMPDIR}/credhub" .
)
fi
;;
*)
echo "Failed to download CredHub: HTTP ${http_status} from ${download_url}" >&2
exit 1
;;
esac

install "$TMPDIR/credhub" /usr/local/bin/credhub
credhub --version
Expand Down
16 changes: 16 additions & 0 deletions src/fly-cli/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## Architectures and version pinning

Downloads match the container architecture on AMD64 and ARM64. By default, Fly is
downloaded from the configured Concourse `url` and matches that server's version.

Set `version` to an exact Concourse release to download its GitHub release archive
instead. This allows building a bootstrap container before your Concourse server
exists and makes the installed CLI version reproducible.

```json
"features": {
"ghcr.io/nulldriver/devcontainer-features/fly-cli:1": {
"version": "8.3.2"
}
}
```
13 changes: 11 additions & 2 deletions src/fly-cli/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
{
"name": "Concourse Fly CLI",
"id": "fly-cli",
"version": "1.0.0",
"version": "1.1.0",
"description": "Installs the Concourse Fly CLI.",
"options": {
"url": {
"type": "string",
"default": "https://ci.concourse-ci.org",
"description": "Concourse URL to download the Fly CLI from",
"description": "Concourse URL to download the Fly CLI from when version is empty",
"proposals": [
"",
"https://ci.concourse-ci.org"
]
},
"version": {
"type": "string",
"default": "",
"description": "Optional exact Concourse release version to download from GitHub instead of the configured URL.",
"proposals": [
"",
"8.3.2"
]
}
},
"installsAfter": [
Expand Down
12 changes: 9 additions & 3 deletions src/fly-cli/install.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/usr/bin/env bash

: ${URL:=https://ci.concourse-ci.org}
: ${VERSION:=""}

USERNAME=${USERNAME:-${_REMOTE_USER:-"automatic"}}

set -e
set -eo pipefail

export TMPDIR=$(mktemp -d /tmp/feature.XXXXXX)
trap "rm -rf $TMPDIR" EXIT
Expand Down Expand Up @@ -59,11 +60,16 @@ export DEBIAN_FRONTEND=noninteractive

### BEGIN install

# Soft version matching
check_packages curl ca-certificates
echo "Downloading fly..."
curl -SL "$URL/api/v1/cli?arch=amd64&platform=linux" -o "$TMPDIR/fly"
if [ -n "${VERSION}" ]; then
VERSION="${VERSION#v}"
curl -fsSL --retry 3 "https://github.com/concourse/concourse/releases/download/v${VERSION}/fly-${VERSION}-linux-${architecture}.tgz" | tar -zxC "$TMPDIR"
else
curl -fsSL --retry 3 "${URL%/}/api/v1/cli?arch=${architecture}&platform=linux" -o "$TMPDIR/fly"
fi
install "$TMPDIR/fly" /usr/local/bin/fly
fly --version

### END install

Expand Down
5 changes: 5 additions & 0 deletions test/cf-cli/latest_release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash

set -e

source "$(dirname "$0")/test.sh"
6 changes: 6 additions & 0 deletions test/cf-cli/native_architecture.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash

set -e

export EXPECTED_VERSION="8.19.0"
source "$(dirname "$0")/test.sh"
14 changes: 14 additions & 0 deletions test/cf-cli/scenarios.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
{
"latest_release": {
"image": "mcr.microsoft.com/devcontainers/base:resolute",
"features": {
"cf-cli": {}
}
},
"native_architecture": {
"image": "mcr.microsoft.com/devcontainers/base:resolute",
"features": {
"cf-cli": {
"version": "8.19.0"
}
}
},
"install_cf_cli_specific_version": {
"image": "mcr.microsoft.com/devcontainers/base:debian",
"features": {
Expand Down
9 changes: 9 additions & 0 deletions test/cf-cli/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,14 @@ set -e
source dev-container-features-test-lib

check "cf version" cf version
if [ -n "${EXPECTED_VERSION:-}" ]; then
check "pinned cf version" bash -c 'cf version | grep -F "$EXPECTED_VERSION"'
fi

case "$(dpkg --print-architecture)" in
amd64) expected_machine=62 ;;
arm64) expected_machine=183 ;;
esac
check "native cf ELF architecture" test "$(od -An -tu2 -j18 -N2 "$(command -v cf)" | tr -d ' ')" = "${expected_machine}"

reportResults
6 changes: 6 additions & 0 deletions test/credhub-cli/native_architecture.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash

set -e

export EXPECTED_VERSION="2.9.62"
source "$(dirname "$0")/test.sh"
16 changes: 16 additions & 0 deletions test/credhub-cli/scenarios.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
{
"native_architecture": {
"image": "mcr.microsoft.com/devcontainers/base:resolute",
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.27.2",
"installGoTools": false
},
"credhub-cli": {
"version": "2.9.62"
}
}
},
"install_credhub_cli_specific_version": {
"image": "mcr.microsoft.com/devcontainers/base:debian",
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.27.2",
"installGoTools": false
},
"credhub-cli": {
"version": "2.9.50"
}
Expand Down
9 changes: 9 additions & 0 deletions test/credhub-cli/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,14 @@ set -e
source dev-container-features-test-lib

check "credhub version" credhub --version
if [ -n "${EXPECTED_VERSION:-}" ]; then
check "pinned credhub version" bash -c 'credhub --version | grep -F "$EXPECTED_VERSION"'
fi

case "$(dpkg --print-architecture)" in
amd64) expected_machine=62 ;;
arm64) expected_machine=183 ;;
esac
check "native credhub ELF architecture" test "$(od -An -tu2 -j18 -N2 "$(command -v credhub)" | tr -d ' ')" = "${expected_machine}"

reportResults
6 changes: 6 additions & 0 deletions test/fly-cli/native_architecture.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash

set -e

export EXPECTED_VERSION="8.3.2"
source "$(dirname "$0")/test.sh"
9 changes: 9 additions & 0 deletions test/fly-cli/scenarios.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
{
"native_architecture": {
"image": "mcr.microsoft.com/devcontainers/base:resolute",
"features": {
"fly-cli": {
"version": "8.3.2",
"url": "https://unused.invalid"
}
}
},
"install_fly_cli_specific_url": {
"image": "mcr.microsoft.com/devcontainers/base:debian",
"features": {
Expand Down
9 changes: 9 additions & 0 deletions test/fly-cli/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,14 @@ set -e
source dev-container-features-test-lib

check "fly version" fly --version
if [ -n "${EXPECTED_VERSION:-}" ]; then
check "pinned fly version" bash -c 'fly --version | grep -F "$EXPECTED_VERSION"'
fi

case "$(dpkg --print-architecture)" in
amd64) expected_machine=62 ;;
arm64) expected_machine=183 ;;
esac
check "native fly ELF architecture" test "$(od -An -tu2 -j18 -N2 "$(command -v fly)" | tr -d ' ')" = "${expected_machine}"

reportResults
Loading