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
81 changes: 78 additions & 3 deletions scripts/devloop_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ equals() {
[[ "$actual" == "$expected" ]] || fail "$label expected [$expected], got [$actual]"
}

bash -n "$REPO_ROOT/devloop" "$SCRIPTS_DIR/install.sh" "$SCRIPTS_DIR/uninstall.sh" "$SCRIPTS_DIR/skill_helpers.sh" "$SCRIPTS_DIR/release.sh" "$REMOTE_INSTALLER"
bash -n "$REPO_ROOT/devloop" "$SCRIPTS_DIR/install.sh" "$SCRIPTS_DIR/uninstall.sh" "$SCRIPTS_DIR/skill_helpers.sh" "$SCRIPTS_DIR/release.sh" "$REMOTE_INSTALLER" "$REPO_ROOT/site/public/install"
ok "bash syntax"

DEVLOOP_LIB=1
Expand Down Expand Up @@ -114,6 +114,77 @@ ok "skill metadata"
work=$(mktemp -d "${TMPDIR:-/tmp}/devloop-test.XXXXXX")
trap 'rm -rf "$work"' EXIT

equals "$(sed -n '1p' "$REPO_ROOT/site/public/VERSION")" "$version" "site VERSION matches root VERSION"
ok "site version file"

bootstrap_bin="$work/install-bootstrap-bin"
bootstrap_log="$work/install-bootstrap.log"
mkdir -p "$bootstrap_bin"
cat > "$bootstrap_bin/curl" <<'CURL'
#!/usr/bin/env bash
set -euo pipefail
if [ "${1:-}" != "-fsSL" ]; then
printf 'unexpected curl args: %s\n' "$*" >&2
exit 1
fi
url="${2:-}"
printf '%s\n' "$url" >> "$DEVLOOP_BOOTSTRAP_LOG"
case "$url" in
https://version.example/devloop)
printf '%s\n' '9.8.7'
;;
https://raw.example/devloop/v*/scripts/install.remote.sh)
cat <<'SCRIPT'
#!/usr/bin/env bash
printf 'installer args:'
for arg in "$@"; do printf ' <%s>' "$arg"; done
printf '\n'
SCRIPT
;;
*)
printf 'unexpected url: %s\n' "$url" >&2
exit 1
;;
esac
CURL
chmod +x "$bootstrap_bin/curl"
bootstrap_output="$(
DEVLOOP_BOOTSTRAP_LOG="$bootstrap_log" \
DEVLOOP_VERSION_URL="https://version.example/devloop" \
DEVLOOP_RAW_BASE_URL="https://raw.example/devloop" \
PATH="$bootstrap_bin:/usr/bin:/bin:/usr/sbin:/sbin" \
bash "$REPO_ROOT/site/public/install" --dry-run
)"
contains "$bootstrap_output" "installer args: <--version> <9.8.7> <--dry-run>" "site install bootstrap"
contains "$(cat "$bootstrap_log")" "https://version.example/devloop" "site install bootstrap version"
contains "$(cat "$bootstrap_log")" "https://raw.example/devloop/v9.8.7/scripts/install.remote.sh" "site install bootstrap installer"
: > "$bootstrap_log"
bootstrap_pinned_output="$(
DEVLOOP_BOOTSTRAP_LOG="$bootstrap_log" \
DEVLOOP_VERSION_URL="https://version.example/devloop" \
DEVLOOP_RAW_BASE_URL="https://raw.example/devloop" \
PATH="$bootstrap_bin:/usr/bin:/bin:/usr/sbin:/sbin" \
bash "$REPO_ROOT/site/public/install" --version=1.2.3 --dry-run
)"
contains "$bootstrap_pinned_output" "installer args: <--version> <1.2.3> <--dry-run>" "site install bootstrap pinned"
not_contains "$(cat "$bootstrap_log")" "https://version.example/devloop" "site install bootstrap pinned"
contains "$(cat "$bootstrap_log")" "https://raw.example/devloop/v1.2.3/scripts/install.remote.sh" "site install bootstrap pinned"
: > "$bootstrap_log"
if bootstrap_bad_version_output="$(
DEVLOOP_BOOTSTRAP_LOG="$bootstrap_log" \
DEVLOOP_VERSION_URL="https://version.example/devloop" \
DEVLOOP_RAW_BASE_URL="https://raw.example/devloop" \
PATH="$bootstrap_bin:/usr/bin:/bin:/usr/sbin:/sbin" \
bash "$REPO_ROOT/site/public/install" --version 2>&1
)"; then
printf '%s\n' "$bootstrap_bad_version_output" >&2
fail "site install bootstrap accepted bare --version"
fi
contains "$bootstrap_bad_version_output" "error: --version requires a value" "site install bootstrap bare version"
not_contains "$(cat "$bootstrap_log")" "https://version.example/devloop" "site install bootstrap bare version"
not_contains "$(cat "$bootstrap_log")" "scripts/install.remote.sh" "site install bootstrap bare version"
ok "site install bootstrap"

make_remote_release() {
local version="$1"
local releases="$2"
Expand Down Expand Up @@ -545,9 +616,13 @@ ok "pure helpers"
return 0
}
ROOT="$work/release-root"
mkdir -p "$ROOT"
mkdir -p "$ROOT/site/public"
git init -q "$ROOT"
release_write_version_files "9.9.8"
equals "$(sed -n '1p' "$ROOT/VERSION")" "9.9.8" "release writes root version"
equals "$(sed -n '1p' "$ROOT/site/public/VERSION")" "9.9.8" "release writes site version"
printf '%s\n' "9.9.9" > "$ROOT/VERSION"
printf '%s\n' "9.9.9" > "$ROOT/site/public/VERSION"
dry_run_output="$(release_main "patch" --dry-run)" || fail "release dry-run required git-cliff"
contains "$dry_run_output" "next: 9.9.10 (v9.9.10)" "release dry-run"
contains "$dry_run_output" "would tag: v9.9.10" "release dry-run"
Expand All @@ -557,7 +632,7 @@ ok "pure helpers"
contains "$publish_dry_run_output" "would create GitHub release: gh release create v9.9.10 --verify-tag --generate-notes" "release publish dry-run"
git -C "$ROOT" config user.email devloop-test@example.com
git -C "$ROOT" config user.name "devloop test"
git -C "$ROOT" add VERSION
git -C "$ROOT" add VERSION site/public/VERSION
git -C "$ROOT" commit -q -m init
release_assert_clean_tree || fail "release clean tree rejected"
printf '%s\n' "dirty" > "$ROOT/dirty"
Expand Down
12 changes: 11 additions & 1 deletion scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ release_current_version() {
sed -n '1p' "$ROOT/VERSION" 2>/dev/null || true
}

release_write_version_files() {
local version="$1"
local site_version="$ROOT/site/public/VERSION"
printf '%s\n' "$version" > "$ROOT/VERSION"
if [ -d "$(dirname "$site_version")" ]; then
printf '%s\n' "$version" > "$site_version"
fi
}

release_next_version() {
local bump="$1"
local current="$2"
Expand Down Expand Up @@ -245,8 +254,9 @@ release_main() {
if [ "$publish" = true ] || [ "$push" = true ]; then release_assert_push_branch; fi

bash "$ROOT/scripts/devloop_test.sh"
printf '%s\n' "$version" > "$ROOT/VERSION"
release_write_version_files "$version"
git -C "$ROOT" add VERSION
if [ -f "$ROOT/site/public/VERSION" ]; then git -C "$ROOT" add site/public/VERSION; fi
git -C "$ROOT" commit -m "chore: release $version"
git -C "$ROOT" tag -a "$tag" -m "devloop $version"
# Regenerate the changelog after the tag exists and render from the tagged
Expand Down
16 changes: 11 additions & 5 deletions site/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,14 @@ so deployment is only complete once two things are true:

1. `devloop.sh` points at this Pages project (Pages project > Custom domains >
add `devloop.sh`; Cloudflare provisions the TLS cert).
2. `https://devloop.sh/install` serves the install script as `text/plain`. Until
the real installer exists, either drop an `install` file into the deployed
output or add a redirect to the raw `install.sh` in the CLI repo. With Pages,
a `dist/_redirects` line like `/install https://raw.githubusercontent.com/satyaborg/devloop/main/install.sh 200`
proxies it.
2. `https://devloop.sh/install` serves `public/install`, a small bootstrap that
reads `public/VERSION` from `https://devloop.sh/VERSION` and executes the
installer from the matching Git tag.

For releases, push the Git tag and confirm Pages has deployed the release
commit. A stale Pages deploy keeps new installs on the previous version, while a
`VERSION` file that points at an unpushed tag makes `/install` fail when it
fetches the tagged installer.

## Other static hosts

Expand All @@ -75,6 +78,9 @@ site/
index.html source page (vite entry)
src/input.css tailwind entry + theme tokens + @font-face
vite.config.js vite + @tailwindcss/vite plugin
public/_headers Cloudflare Pages headers for extensionless files
public/install VERSION-based bootstrap served at /install
public/VERSION release version served at /VERSION
public/fonts/ JetBrains Mono woff2 (400, 700), served at /fonts/
dist/ build output (gitignored)
```
1 change: 1 addition & 0 deletions site/public/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.4.1
2 changes: 2 additions & 0 deletions site/public/_headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/install
Content-Type: text/plain; charset=utf-8
1 change: 0 additions & 1 deletion site/public/_redirects

This file was deleted.

95 changes: 95 additions & 0 deletions site/public/install
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/env bash
set -euo pipefail

GITHUB_REPO="${DEVLOOP_GITHUB_REPO:-satyaborg/devloop}"
VERSION_URL="${DEVLOOP_VERSION_URL:-https://devloop.sh/VERSION}"
RAW_BASE_URL="${DEVLOOP_RAW_BASE_URL:-https://raw.githubusercontent.com/$GITHUB_REPO}"

fail() {
printf 'error: %s\n' "$*" >&2
exit 1
}

normalize_version() {
local version="$1"
version="${version#v}"
version="$(printf '%s\n' "$version" | sed -n '1p' | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')"
if ! printf '%s\n' "$version" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$'; then
fail "invalid version: $1"
fi
printf '%s\n' "$version"
}

require_curl() {
if ! command -v curl >/dev/null 2>&1; then
fail "missing curl; install curl or download the release installer manually"
fi
}

requested_version() {
while [ "$#" -gt 0 ]; do
case "$1" in
--version)
[ "$#" -ge 2 ] || fail "--version requires a value"
printf '%s\n' "$2"
return 0
;;
--version=*)
printf '%s\n' "${1#--version=}"
return 0
;;
esac
shift
done
return 1
}

validate_requested_version_args() {
while [ "$#" -gt 0 ]; do
case "$1" in
--version)
[ "$#" -ge 2 ] || fail "--version requires a value"
shift
;;
esac
shift
done
}

site_version() {
local version
version="$(curl -fsSL "$VERSION_URL")" || fail "failed to resolve Devloop version"
normalize_version "$version"
}

main() {
local version installer_url
local args=()
require_curl
validate_requested_version_args "$@"
if version="$(requested_version "$@")"; then
version="$(normalize_version "$version")"
else
version="$(site_version)"
fi

args=(--version "$version")
while [ "$#" -gt 0 ]; do
case "$1" in
--version)
shift
if [ "$#" -gt 0 ]; then shift; fi
;;
--version=*) shift ;;
*)
args+=("$1")
shift
;;
esac
done

installer_url="$RAW_BASE_URL/v$version/scripts/install.remote.sh"
curl -fsSL "$installer_url" | bash -s -- "${args[@]}"
}

main "$@"
Loading