diff --git a/docs/contributing/feature-stages.md b/docs/contributing/feature-stages.md index 25b37bbc01..40926932ca 100644 --- a/docs/contributing/feature-stages.md +++ b/docs/contributing/feature-stages.md @@ -27,5 +27,12 @@ coder server --experiments=feature1,feature2 # Alternatively, use the `CODER_EXPERIMENTS` environment variable. ``` -For a list of all experiments, refer to the -[codersdk reference](https://pkg.go.dev/github.com/coder/coder/v2/codersdk#Experiment). +## Available experimental features + + + + +Currently no experimental features are available in the latest mainline or +stable release. + + diff --git a/scripts/release.sh b/scripts/release.sh index 347c386426..8329b6ec6a 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -374,6 +374,7 @@ You can follow the release progress [here](https://github.com/coder/coder/action create_pr_stash=1 fi maybedryrun "${dry_run}" git checkout -b "${pr_branch}" "${remote}/${branch}" + maybedryrun "${dry_run}" execrelative ./release/docs_update_experiments.sh execrelative go run ./release autoversion --channel "${channel}" "${new_version}" --dry-run="${dry_run}" maybedryrun "${dry_run}" git add docs maybedryrun "${dry_run}" git commit -m "${title}" diff --git a/scripts/release/docs_update_experiments.sh b/scripts/release/docs_update_experiments.sh new file mode 100755 index 0000000000..2168fc4d11 --- /dev/null +++ b/scripts/release/docs_update_experiments.sh @@ -0,0 +1,177 @@ +#!/usr/bin/env bash + +# Usage: ./docs_update_experiments.sh +# +# This script updates the available experimental features in the documentation. +# It fetches the latest mainline and stable releases to extract the available +# experiments and their descriptions. The script will update the +# feature-stages.md file with a table of the latest experimental features. + +set -euo pipefail +# shellcheck source=scripts/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")/../lib.sh" +cdroot + +# From install.sh +echo_latest_stable_version() { + # https://gist.github.com/lukechilds/a83e1d7127b78fef38c2914c4ececc3c#gistcomment-2758860 + version="$(curl -fsSLI -o /dev/null -w "%{url_effective}" https://github.com/coder/coder/releases/latest)" + version="${version#https://github.com/coder/coder/releases/tag/v}" + echo "v${version}" +} + +echo_latest_mainline_version() { + # Fetch the releases from the GitHub API, sort by version number, + # and take the first result. Note that we're sorting by space- + # separated numbers and without utilizing the sort -V flag for the + # best compatibility. + echo "v$( + curl -fsSL https://api.github.com/repos/coder/coder/releases | + awk -F'"' '/"tag_name"/ {print $4}' | + tr -d v | + tr . ' ' | + sort -k1,1nr -k2,2nr -k3,3nr | + head -n1 | + tr ' ' . + )" +} + +# For testing or including experiments from `main`. +echo_latest_main_version() { + echo origin/main +} + +sparse_clone_codersdk() { + mkdir -p "${1}" + cd "${1}" + rm -rf "${2}" + git clone --quiet --no-checkout "${PROJECT_ROOT}" "${2}" + cd "${2}" + git sparse-checkout set --no-cone codersdk + git checkout "${3}" -- codersdk + echo "${1}/${2}" +} + +parse_all_experiments() { + # Go doc doesn't include inline array comments, so this parsing should be + # good enough. We remove all whitespaces so that we can extract a plain + # string that looks like {}, {ExpA}, or {ExpA,ExpB,}. + # + # Example: ExperimentsAll=Experiments{ExperimentNotifications,ExperimentAutoFillParameters,} + go doc -all -C "${dir}" ./codersdk ExperimentsAll | + tr -d $'\n\t ' | + grep -E -o 'ExperimentsAll=Experiments\{[^}]*\}' | + sed -e 's/.*{\(.*\)}.*/\1/' | + tr ',' '\n' +} + +parse_experiments() { + # Extracts the experiment name and description from the Go doc output. + # The output is in the format: + # + # ||Add new experiments here! + # ExperimentExample|example|This isn't used for anything. + # ExperimentAutoFillParameters|auto-fill-parameters|This should not be taken out of experiments until we have redesigned the feature. + # ExperimentMultiOrganization|multi-organization|Requires organization context for interactions, default org is assumed. + # ExperimentCustomRoles|custom-roles|Allows creating runtime custom roles. + # ExperimentNotifications|notifications|Sends notifications via SMTP and webhooks following certain events. + # ExperimentWorkspaceUsage|workspace-usage|Enables the new workspace usage tracking. + # ||ExperimentTest is an experiment with + # ||a preceding multi line comment!? + # ExperimentTest|test| + # + go doc -all -C "${1}" ./codersdk Experiment | + sed \ + -e 's/\t\(Experiment[^ ]*\)\ \ *Experiment = "\([^"]*\)"\(.*\/\/ \(.*\)\)\?/\1|\2|\4/' \ + -e 's/\t\/\/ \(.*\)/||\1/' | + grep '|' +} + +workdir=build/docs/experiments +dest=docs/contributing/feature-stages.md + +log "Updating available experimental features in ${dest}" + +declare -A experiments=() experiment_tags=() + +for channel in mainline stable; do + log "Fetching experiments from ${channel}" + + tag=$(echo_latest_"${channel}"_version) + dir="$(sparse_clone_codersdk "${workdir}" "${channel}" "${tag}")" + + declare -A all_experiments=() + all_experiments_out="$(parse_all_experiments "${dir}")" + if [[ -n "${all_experiments_out}" ]]; then + readarray -t all_experiments_tmp <<<"${all_experiments_out}" + for exp in "${all_experiments_tmp[@]}"; do + all_experiments[$exp]=1 + done + fi + + # Track preceding/multiline comments. + maybe_desc= + + while read -r line; do + line=${line//$'\n'/} + readarray -d '|' -t parts <<<"$line" + + # Missing var/key, this is a comment or description. + if [[ -z ${parts[0]} ]]; then + maybe_desc+="${parts[2]//$'\n'/ }" + continue + fi + + var="${parts[0]}" + key="${parts[1]}" + desc="${parts[2]}" + desc=${desc//$'\n'/} + + # If desc (trailing comment) is empty, use the preceding/multiline comment. + if [[ -z "${desc}" ]]; then + desc="${maybe_desc% }" + fi + maybe_desc= + + # Skip experiments not listed in ExperimentsAll. + if [[ ! -v all_experiments[$var] ]]; then + log "Skipping ${var}, not listed in ExperimentsAll" + continue + fi + + # Don't overwrite desc, prefer first come, first served (i.e. mainline > stable). + if [[ ! -v experiments[$key] ]]; then + experiments[$key]="$desc" + fi + + # Track the release channels where the experiment is available. + experiment_tags[$key]+="${channel}, " + done < <(parse_experiments "${dir}") +done + +table="$( + if [[ "${#experiments[@]}" -eq 0 ]]; then + echo "Currently no experimental features are available in the latest mainline or stable release." + exit 0 + fi + + echo "| Feature | Description | Available in |" + echo "|---------|-------------|--------------|" + for key in "${!experiments[@]}"; do + desc=${experiments[$key]} + tags=${experiment_tags[$key]%, } + echo "| \`$key\` | $desc | ${tags} |" + done +)" + +# Use awk to print everything outside the BEING/END block and insert the +# table in between. +awk \ + -v table="${table}" \ + 'BEGIN{include=1} /BEGIN: available-experimental-features/{print; print table; include=0} /END: available-experimental-features/{include=1} include' \ + "${dest}" \ + >"${dest}".tmp +mv "${dest}".tmp "${dest}" + +# Format the file for a pretty table (target single file for speed). +(cd site && pnpm exec prettier --cache --write ../"${dest}")