diff --git a/pkgs/applications/editors/vscode/extension-registries/commons/nix-prefetch-vsix-lib/default.nix b/pkgs/applications/editors/vscode/extension-registries/commons/nix-prefetch-vsix-lib/default.nix new file mode 100644 index 0000000000000..1b412f0854716 --- /dev/null +++ b/pkgs/applications/editors/vscode/extension-registries/commons/nix-prefetch-vsix-lib/default.nix @@ -0,0 +1,66 @@ +{ lib +, stdenvNoCC +, shellcheck +, coreutils +, curl +, jq +, nix +, unzip +}: + +stdenvNoCC.mkDerivation { + pname = "nix-prefetch-vsix-lib"; + version = "0.1.0"; + + preferLocalBuild = true; + + src = [ + ./nix-prefetch-vsix-lib.sh + ]; + + dontUnpack = true; + + installPhase = '' + runHook preInstall + mkdir -p "$out/bin" + substitute "$src" "$out/bin/$(stripHash "$src")" \ + --replace "###PLACEHOLDER_PATH_PREFIXING###" "PATH=\"${lib.makeBinPath [ + coreutils + curl + jq + nix + unzip + ]}\''${PATH:+:}\''${PATH-}\"; export PATH" + runHook postInstall + ''; + + doInstallCheck = true; + + installCheckInputs = [ + shellcheck + ]; + + installCheckPhase = '' + runHook preInstallCheck + + # Run through shellcheck + while IFS= read -r -d "" file; do + shellcheck --shell=bash "$file" + done < <(find "$out/bin" -mindepth 1 -type f,l -print0) + + # Test sourcing + ( + set -eu -o pipefail + source "$out/bin/nix-prefetch-vsix-lib.sh" + ) + + runHook postInstallCheck + ''; + + meta = with lib; { + description = "Bash library to construct a VSCode extension fetcher"; + license = licenses.mit; + platforms = platforms.all; + maintainers = with maintainers; [ ShamrockLee ]; + }; +} diff --git a/pkgs/applications/editors/vscode/extension-registries/commons/nix-prefetch-vsix-lib/nix-prefetch-vsix-lib.sh b/pkgs/applications/editors/vscode/extension-registries/commons/nix-prefetch-vsix-lib/nix-prefetch-vsix-lib.sh new file mode 100644 index 0000000000000..4bc13db118ba4 --- /dev/null +++ b/pkgs/applications/editors/vscode/extension-registries/commons/nix-prefetch-vsix-lib/nix-prefetch-vsix-lib.sh @@ -0,0 +1,446 @@ +# This Bash library contains components to construct a nix-prefetcher of +# Visual Studio Code extensions in the form of VSIX files +# in a modular (Makefile-like) way. +# +# When writing a prefetcher for an extension registry, +# Use this derivation as build inputs, source this file by name, +# and override some functions if need be. +# +# A function in the form `prepare_foo` +# guarentees that the shell variable FOO are assigned with the value needed. +# Call it (in the main shell, not in the sub-shells) before accessing FOO. +# This way, each function can be overrided seperately, +# and only nessesary steps will be executed. +# +# A file path is calculated by `prepare_foo_path_expected` +# and realized by `perpare_foo_path`. +# For example, calling prepare_vsix_path will realize the VSIX file (*.vsix), +# and prepare_manifest_path the manifest file (package.json). +# +# Part of the code is adapted from get_vsixpkg() of the original update_install_exts.sh + +###PLACEHOLDER_PATH_PREFIXING### + +function reset_variables { + # variables to be `prepare_`d + unset COMPACT + unset CONFIGURATION + unset EXTTMP + unset HASH_FORMAT + unset HASH_ALGO + unset NO_HASH + unset NO_META + unset PRINT_CONFIGURATION + unset VSIX_HASH + unset VSIX_IDENTIFIER + unset VSIX_NAME + unset VSIX_PATH + unset VSIX_PATH_EXPECTED + unset VSIX_PUBLISHER + unset VSIX_URL + unset VSIX_VERSION + unset VSIX_VERSION_FETCHED + unset VSIX_VERSION_SPECIFIED +} + +if (("$#")) && [[ "${1-}" != "--no-reset" ]]; then + reset_variables +fi + +# Helper to just fail with a message and non-zero exit code. +function fail { + echo "$1" >&2 + exit 1 +} + +# Define a new function named NEW_NAME +# with the definition of function named OLD_NAME. +# This provides access to the original function +# while overriding. +# See https://mharrison.org/post/bashfunctionoverride/ +function save_function { + local OLD_NAME NEW_NAME FUNCTION_COMMAND + OLD_NAME="$1" + NEW_NAME="$2" + FUNCTION_COMMAND=$(declare -f "$OLD_NAME") + eval "$NEW_NAME${FUNCTION_COMMAND#"$OLD_NAME"}" +} + +# escape doublequotes ("\""), used to output conformed json strings. +function escape_doublequotes { + local RESULT + RESULT="${1//\\/\\\\}" + echo "${RESULT//\"/\\\"/}" +} + +# Split shorthands and re-add them into "$@" +# -abc -d -> -a -b -c -d +function manage_shorthands { + local STR_RAW="${1:1}" + shift + local -i i=0 + # Parameterized expansions of for loop + # doesn't work with 'set -u' (throw unbound variable error) + # {1..2} doesn't work with variables as the upper bound + # So use 'seq' from 'coreutils' + for i in $(seq 0 $((${#STR_RAW} - 1))); do + set -- "-${STR_RAW:-$i:1}" "$@" + done +} + +# Quietly but delicately curl down the file, blowing up at the first sign of trouble. +function download_file { + local FILE_PATH="$1" + local URL="$2" + if ! curl --silent --show-error --fail -L -o "$FILE_PATH" "$URL" || [[ ! -e "$FILE_PATH" ]]; then + echo "download_file: Failed to download $FILE_PATH from $URL." >&2 + return 1 + fi +} + +# Create a tempdir for the extension download. +function create_exttmp { + EXTTMP=$(mktemp -d -t vscode_exts_XXXXXXXX) +} + +function prepare_exttmp { + if [[ -z "${EXTTMP-}" ]]; then + echo "create_exttmp must be run first for security reasons" >&2 + return 1 + fi +} + +# The publisher part of the extension identifier +function prepare_vsix_publisher { + if [[ -z "${VSIX_PUBLISHER-}" ]]; then + echo "VSIX_PUBLISHER: Variable empty or unavailable." >&2 + return 1 + fi +} + +# The name part of the extension identifier +function prepare_vsix_name { + if [[ -z "${VSIX_NAME-}" ]]; then + echo "VSIX_NAME: Variable empty or unavailable." >&2 + return 1 + fi +} + +# The specified version, often default to latest (registry-dependent) +function prepare_vsix_version_specified { + if [[ -z "${VSIX_VERSION_SPECIFIED-}" ]]; then + echo "VSIX_VERSION_SPECIFIED: Variable empty or unavailable." >&2 + return 1 + fi +} + +# The extension identifier +function prepare_vsix_identifier { + prepare_vsix_publisher + prepare_vsix_name + VSIX_IDENTIFIER="$VSIX_PUBLISHER.$VSIX_NAME" +} + +# URL to download the VSIX file +function prepare_vsix_url { + echo "prepare_vsix_url: Bash function not implemented." >&2 + return 1 +} + +# Expected path of the VSIX file +function prepare_vsix_path_expected { + prepare_exttmp + prepare_vsix_identifier + VSIX_PATH_EXPECTED="$EXTTMP/$VSIX_IDENTIFIER.vsix" +} + +# Download the VSIX file +function download_vsix { + prepare_vsix_path_expected + prepare_vsix_url + download_file "$VSIX_PATH_EXPECTED" "$VSIX_URL" + VSIX_PATH="$VSIX_PATH_EXPECTED" +} + +# Realized path of the VSIX file +function prepare_vsix_path { + if [[ -z "${VSIX_PATH-}" ]]; then + download_vsix + fi +} + +# Add the downloadded VSIX file to the Nix store +function add_vsix_to_store { + prepare_vsix_identifier + prepare_vsix_version_fetched + local PROPER_NAME="$VSIX_IDENTIFIER-$VSIX_VERSION_FETCHED" + prepare_vsix_platform + if [[ "$VSIX_PLATFORM" != "universal" ]]; then + PROPER_NAME="$PROPER_NAME@$VSIX_PLATFORM" + fi + PROPER_NAME="$PROPER_NAME.vsix" + prepare_vsix_path + local PROPER_PATH + PROPER_PATH="$(dirname "$VSIX_PATH")/$PROPER_NAME" + cp --link "$VSIX_PATH" "$PROPER_PATH" + nix-store --add-fixed sha256 "$PROPER_PATH" >/dev/null + unlink "$PROPER_PATH" +} + +function prepare_hash_format { + if [[ -z "${HASH_FORMAT-}" ]]; then + HASH_FORMAT="sri" + fi +} + +function prepare_hash_algo { + if [[ -z "${HASH_ALGO-}" ]]; then + HASH_ALGO="sha256" + fi +} + +# Compute the hash directly +function compute_vsix_hash { + if [[ -z "${VSIX_HASH-}" ]]; then + prepare_vsix_path + prepare_hash_format + prepare_hash_algo + VSIX_HASH=$(nix --extra-experimental-features "nix-command" hash file "--$HASH_FORMAT" --type "$HASH_ALGO" "$VSIX_PATH") + fi +} + +function prepare_vsix_hash { + compute_vsix_hash +} + +function prepare_manifest_path_expected { + prepare_exttmp + MANIFEST_PATH_EXPECTED="$EXTTMP/package.json" +} + +# Unpack the extension manifest from the VSIX file +function unpack_manifest { + prepare_vsix_path + prepare_manifest_path_expected + unzip -qp "$VSIX_PATH" "extension/package.json" >"$MANIFEST_PATH_EXPECTED" + MANIFEST_PATH="$MANIFEST_PATH_EXPECTED" +} + +# Realize the extension manifest +function prepare_manifest_path { + if [[ -z "${MANIFEST_PATH-}" ]]; then + unpack_manifest + fi +} + +# Get an attribute from a JSON file and assign to a shell variable. +# when the shell variable is empty or unset. +# +# Use as +# refget_from_varname_json VARNAME_JSON VARNAME [+]ATTRNAME [EXLCUSION1 [EXCLUSION2 ...] ] +# +# Prefix the attribute name with `+` +# for JSON text output instead of raw string output +# (omitting `-r` for `jq`). +# +# The exclusion arguments, if given, +# forces the shell variable to be assign as "null" +# when the extracted value is identical to one of them. +function refget_from_varname_json { + local VARNAME_JSON_PATH VARNAME ATTRNAME IS_RAW + VARNAME_JSON_PATH="$1" + VARNAME="$2" + ATTRNAME="$3" + IS_RAW=1 + if [[ -n "${!VARNAME-}" ]]; then + return 0 + fi + if [[ "${ATTRNAME::1}" == "+" ]]; then + ATTRNAME="${ATTRNAME:1}" + IS_RAW=0 + fi + local -a EXCLUSION_ARRAY + EXCLUSION_ARRAY=() + if (("$#" > 3)); then + shift 3 + EXCLUSION_ARRAY=("$@") + fi + local -a FLAGS_ARRAY + FLAGS_ARRAY=("-c") + if ((IS_RAW)); then + FLAGS_ARRAY+=("-r") + fi + "prepare_${VARNAME_JSON_PATH,,}" + declare -g "$VARNAME"="$(jq "${FLAGS_ARRAY[@]}" ".$ATTRNAME" "${!VARNAME_JSON_PATH}")" + local EXCLUSION + for EXCLUSION in "${EXCLUSION_ARRAY[@]}"; do + if [[ "${!VARNAME}" == "$EXCLUSION" ]]; then + declare -g "$VARNAME"="null" + break + fi + done +} + +# Get an attribute from package.json and assign to a shell variable. +# when the shell variable is empty or unset. +function refget_from_manifest { + refget_from_varname_json MANIFEST_PATH "$@" +} + +function prepare_vsix_version_fetched { + refget_from_manifest VSIX_VERSION_FETCHED version +} + +function prepare_vsix_version { + if [[ -z "${VSIX_VERSION-}" ]]; then + prepare_vsix_version_fetched + if + [[ "$VSIX_VERSION_SPECIFIED" != "latest" ]] && + [[ "$VSIX_VERSION_SPECIFIED" != "pre" ]] && + [[ "$VSIX_VERSION_SPECIFIED" != "$VSIX_VERSION_FETCHED" ]] + then + echo "prepare_vsix_version: Fetched version desn't match the specified vesion" >&2 + return 1 + else + VSIX_VERSION="$VSIX_VERSION_FETCHED" + fi + fi +} + +function prepare_meta_description { + refget_from_manifest META_DESCRIPTION description "" +} + +function prepare_meta_homepage { + refget_from_manifest META_HOMEPAGE homepage "" +} + +function prepare_meta_license_raw { + refget_from_manifest META_LICENSE_RAW license "" +} + +function prepare_extensionpack { + refget_from_manifest EXTENSIONPACK +extensionPack +} + +function prepare_configuration { + refget_from_manifest CONFIGURATION +contributes.configuration +} + +# Clean up the temp folder whenever exit +function cleanup_exttmp { + if [[ -n "${EXTTMP-}" ]]; then + rm -rf "$EXTTMP" + unset EXTTMP + fi +} + +# Cleanup function, used by `trap` to execute on `EXIT` +function cleanup { + cleanup_exttmp +} + +function prepare_no_hash { + if [[ -z "${NO_HASH-}" ]]; then + NO_HASH=0 + fi +} + +function prepare_no_meta { + if [[ -z "${NO_META-}" ]]; then + NO_META=0 + fi +} + +function prepare_print_configuration { + if [[ -z "${PRINT_CONFIGURATION}" ]]; then + PRINT_CONFIGURATION=0 + fi +} + +# Prepare an associative array DICT_KEY_OUTPUT for keys / variables to print +function prepare_dict_key_output { + declare -g -A DICT_KEY_OUTPUT + if ! ( + set -u + echo "${#DICT_KEY_OUTPUT[@]}" + ) >/dev/null 2>&1; then + # Prevent Bash from considering it unbound + # https://stackoverflow.com/questions/28055346/bash-unbound-variable-array-script-s3-bash + DICT_KEY_OUTPUT=() + fi + if ! (("${#DICT_KEY_OUTPUT[@]}")); then + DICT_KEY_OUTPUT["publisher"]=VSIX_PUBLISHER + DICT_KEY_OUTPUT["name"]=VSIX_NAME + DICT_KEY_OUTPUT["version"]=VSIX_VERSION + prepare_no_hash + if ! ((NO_HASH)); then + prepare_hash_format + if [[ "$HASH_FORMAT" == "sri" ]]; then + DICT_KEY_OUTPUT["hash"]=VSIX_HASH + else + prepare_hash_algo + DICT_KEY_OUTPUT["$HASH_ALGO"]=VSIX_HASH + fi + fi + prepare_no_meta + if ! ((NO_META)); then + local KEYNAME VARNAME + for KEYNAME in description homepage license_raw; do + VARNAME="META_${KEYNAME^^}" # Upper case + DICT_KEY_OUTPUT["$KEYNAME"]="$VARNAME" + done + DICT_KEY_OUTPUT["+extensionpack"]=EXTENSIONPACK + fi + if ((PRINT_CONFIGURATION)); then + DICT_KEY_OUTPUT["+configuration"]=CONFIGURATION + fi + fi +} + +function prepare_compact { + if [[ -z "${COMPACT-}" ]]; then + COMPACT=0 + fi +} + +# Print the output +function print_output { + prepare_dict_key_output + local RESULT KEYNAME VARNAME + # Reverse the keys first, + # since Bash associative array's key display order + # tends to be the reversal of the specification order. + local -a KEYNAME_ARRAY_REVERSED=() + for KEYNAME in "${!DICT_KEY_OUTPUT[@]}"; do + KEYNAME_ARRAY_REVERSED=("$KEYNAME" "${KEYNAME_ARRAY_REVERSED[@]}") + done + for KEYNAME in "${KEYNAME_ARRAY_REVERSED[@]}"; do + # Lower case + "prepare_${DICT_KEY_OUTPUT["$KEYNAME"],,}" + done + RESULT="$( + for KEYNAME in "${KEYNAME_ARRAY_REVERSED[@]}"; do + VARNAME="${DICT_KEY_OUTPUT["$KEYNAME"]}" + VALUE="${!VARNAME}" + if [[ "$VALUE" != "null" ]]; then + if [[ "${KEYNAME::1}" == "+" ]]; then + echo -n "\"${KEYNAME:1}\": $VALUE, " + else + echo -n "\"$KEYNAME\": \"$(escape_doublequotes "$VALUE")\", " + fi + fi + done + echo + )" + # Remove the trailing ", " nd add "{" and "}" + [[ -z "$RESULT" ]] || RESULT="${RESULT::-2}" + # Run through jq + local -a FLAGS_ARRAY + FLAGS_ARRAY=() + prepare_compact + if ((COMPACT)); then + FLAGS_ARRAY+=("-c") + fi + echo "{$RESULT}" | jq "${FLAGS_ARRAY[@]}" +} diff --git a/pkgs/applications/editors/vscode/extension-registries/openvsx/nix-prefetch-openvsx/default.nix b/pkgs/applications/editors/vscode/extension-registries/openvsx/nix-prefetch-openvsx/default.nix new file mode 100644 index 0000000000000..86217ce1da452 --- /dev/null +++ b/pkgs/applications/editors/vscode/extension-registries/openvsx/nix-prefetch-openvsx/default.nix @@ -0,0 +1,68 @@ +{ stdenvNoCC +, lib +, makeWrapper +, shellcheck +, bash +, coreutils +, curl +, jq +, unzip +, nix +, nix-prefetch-vsix-lib +}: + +stdenvNoCC.mkDerivation rec { + pname = "nix-prefetch-openvsx"; + version = "0.1.0"; + + preferLocalBuild = true; + + src = ./nix-prefetch-openvsx; + + dontUnpack = true; + + nativeBuildInputs = [ + makeWrapper + ]; + + buildInputs = [ + bash + ]; + + installPhase = '' + runHook preInstall + mkdir -p "$out/bin" + install -m 755 -T "$src" "$out/bin/nix-prefetch-openvsx" + patchShebangs --host "$out/bin/nix-prefetch-openvsx" + runHook postInstall + ''; + + postFixup = '' + wrapProgram "$out/bin/nix-prefetch-openvsx" \ + --prefix PATH : "${lib.makeBinPath [ + nix-prefetch-vsix-lib + ]}" + ''; + + doInstallCheck = true; + + installCheckInputs = [ + shellcheck + nix-prefetch-vsix-lib + ]; + + installCheckPhase = '' + runHook preInstallCheck + while IFS= read -r -d "" file; do + shellcheck -x -P "$PATH" "$file" + done < <(find "$out/bin" -mindepth 1 -type f,l -print0) + runHook postInstallCheck + ''; + + meta = with lib; { + description = "Prefetch vscode extensions from Open VSX Registry"; + license = licenses.mit; + platforms = platforms.all; + maintainers = with maintainers; [ ShamrockLee ]; + }; +} diff --git a/pkgs/applications/editors/vscode/extension-registries/openvsx/nix-prefetch-openvsx/nix-prefetch-openvsx b/pkgs/applications/editors/vscode/extension-registries/openvsx/nix-prefetch-openvsx/nix-prefetch-openvsx new file mode 100755 index 0000000000000..8c3c6debb2067 --- /dev/null +++ b/pkgs/applications/editors/vscode/extension-registries/openvsx/nix-prefetch-openvsx/nix-prefetch-openvsx @@ -0,0 +1,387 @@ +#!/usr/bin/env bash + +# Adapt from get_vsixpkg() of the original update_install_exts.sh + +set -eu -o pipefail + +declare -a NONFLAG_ARGS=() + +source nix-prefetch-vsix-lib.sh + +unset DOMAIN +function prepare_domain { + if [[ -z "${DOMAIN-}" ]]; then + echo "Variable DOMAIN not defined." >&2 + return 1 + fi +} + +unset VSIX_PLATFORM +function prepare_vsix_platform { + if [[ -z "${VSIX_PLATFORM-}" ]]; then + echo "Variable VSIX_PLATFORM not defined." >&2 + return 1 + fi +} + +function prepare_vsix_metadata_url { + if [[ -z "${VSIX_METADATA_URL-}" ]]; then + prepare_domain + prepare_vsix_publisher + prepare_vsix_name + prepare_vsix_platform + prepare_vsix_version_specified + VSIX_METADATA_URL="$DOMAIN/api/$VSIX_PUBLISHER/$VSIX_NAME/$VSIX_PLATFORM/$VSIX_VERSION_SPECIFIED" + fi +} + +function prepare_vsix_metadata_path_expected { + if [[ -z "${VSIX_METADATA_PATH_EXPECTED-}" ]]; then + prepare_exttmp + prepare_vsix_identifier + VSIX_METADATA_PATH_EXPECTED="$EXTTMP/${VSIX_IDENTIFIER}_meta.json" + fi +} + +# Extension-specific metadata fetched from the Open VSX API +function prepare_vsix_metadata_path { + if [[ -z "${VSIX_METADATA_PATH-}" ]]; then + prepare_vsix_metadata_path_expected + prepare_vsix_metadata_url + download_file "$VSIX_METADATA_PATH_EXPECTED" "$VSIX_METADATA_URL" + VSIX_METADATA_PATH="$VSIX_METADATA_PATH_EXPECTED" + fi +} + +function prepare_vsix_version_fetched { + prepare_vsix_metadata_path + if [[ -z "${VSIX_VERSION_FETCHED-}" ]]; then + refget_from_varname_json VSIX_METADATA_PATH VSIX_VERSION_FETCHED version + if [[ "${VSIX_VERSION_FETCHED:-null}" == "null" ]]; then + echo "VSIX_VERSION_FETCHED: invalid value ($VSIX_VERSION_FETCHED)" >&2 + return 1 + fi + fi +} + +function prepare_vsix_url { + if [[ -z "${VSIX_URL-}" ]]; then + prepare_vsix_metadata_url + prepare_vsix_identifier + prepare_vsix_version + VSIX_URL="${VSIX_METADATA_URL}/file/${VSIX_IDENTIFIER}-${VSIX_VERSION}.vsix" + fi +} + +function prepare_manifest_url { + if [[ -z "${VSIX_URL-}" ]]; then + prepare_vsix_metadata_url + prepare_vsix_identifier + prepare_vsix_version + MANIFEST_URL="${VSIX_METADATA_URL}/file/package.json" + fi +} + +function download_manifest { + prepare_manifest_path_expected + prepare_manifest_url + download_file "$MANIFEST_PATH_EXPECTED" "$MANIFEST_URL" + MANIFEST_PATH="$MANIFEST_PATH_EXPECTED" +} + +unset FORCE_QUERY_HASH +function prepare_force_query_hash { + if [[ -z "${FORCE_QUERY_HASH}" ]]; then + FORCE_QUERY_HASH=0 + fi +} + +unset FORCE_COMPUTE_HASH +function prepare_force_compute_hash { + if [[ -z "${FORCE_COMPUTE_HASH}" ]]; then + FORCE_COMPUTE_HASH=0 + fi +} + +unset ADD_TO_STORE +function prepare_add_to_store { + if [[ -z "${ADD_TO_STORE}" ]]; then + ADD_TO_STORE=0 + fi +} + +function prepare_manifest_path { + prepare_add_to_store + prepare_force_compute_hash + if [[ -z "${MANIFEST_PATH-}" ]]; then + if ((ADD_TO_STORE)) || ((FORCE_COMPUTE_HASH)); then + unpack_manifest + else + download_manifest + fi + fi +} + +function query_vsix_hash { + prepare_vsix_metadata_url + prepare_exttmp + prepare_hash_algo + prepare_hash_format + local VSIX_HASH_FILE_URL="${VSIX_METADATA_URL}/file/${HASH_ALGO}" + local VSIX_HASH_FILE_PATH_EXPECTED="${EXTTMP}/${HASH_ALGO}" + download_file "$VSIX_HASH_FILE_PATH_EXPECTED" "$VSIX_HASH_FILE_URL" + VSIX_HASH="$(nix --extra-experimental-features nix-command hash "to-${HASH_FORMAT}" --type "${HASH_ALGO}" "$(cat "$VSIX_HASH_FILE_PATH_EXPECTED")")" + if [[ -z "$VSIX_HASH" ]]; then + echo "query_vsix_hash: failed" >&2 + return 1 + fi + rm "$VSIX_HASH_FILE_PATH_EXPECTED" +} + +function prepare_vsix_hash { + if [[ -z "${VSIX_HASH-}" ]]; then + prepare_force_query_hash + prepare_force_compute_hash + if ((FORCE_QUERY_HASH)); then + query_vsix_hash + elif ((FORCE_COMPUTE_HASH)); then + compute_vsix_hash + else + # Note: The if keyword and logical operator will deprive those not-the-last commands + # of the `set -e` property, so we must ensure that query_vsix_hash returns properly + # without the fail-early warrenty. + # Use the LBYL strategy instead of the `query_vsix_hash || compute_vsix_hash` EAFP. + # See https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html + local _HASH_FILE_URL; + prepare_hash_algo + prepare_manifest_path + _HASH_FILE_URL="$(jq -c -r ".$HASH_ALGO" "$MANIFEST_PATH")" + if [[ "${_HASH_FILE_URL:-null}" != "null" ]]; then + query_vsix_hash + else + compute_vsix_hash + fi + fi + fi +} + +unset NO_PRINT_DOMAIN +function prepare_no_print_domain { + if [[ -z "${NO_PRINT_DOMAIN-}" ]]; then + NO_PRINT_DOMAIN=0 + fi +} + +unset NO_PRINT_PLATFORM +function prepare_no_print_platform { + if [[ -z "${NO_PRINT_PLATFORM-}" ]]; then + NO_PRINT_PLATFORM=0 + fi +} + +save_function prepare_dict_key_output prepare_dict_key_output_orig +function prepare_dict_key_output { + prepare_dict_key_output_orig + prepare_no_print_domain + if ! ((NO_PRINT_DOMAIN)); then + prepare_domain + DICT_KEY_OUTPUT[domain]=DOMAIN + fi + prepare_no_print_platform + if ! ((NO_PRINT_PLATFORM)); then + prepare_vsix_platform + DICT_KEY_OUTPUT[platform]=VSIX_PLATFORM + fi +} + +VSIX_PUBLISHER="" +VSIX_NAME="" +VSIX_VERSION="" +VSIX_VERSION_SPECIFIED="" +VSIX_VERSION_FETCHED="" +COMPACT=0 +ADD_TO_STORE=0 +NO_HASH=0 +NO_META=0 +PRINT_CONFIGURATION=0 + +DOMAIN="https://open-vsx.org" +VSIX_PLATFORM="universal" +NO_PRINT_DOMAIN=0 +NO_PRINT_PLATFORM=0 +FORCE_QUERY_HASH=0 +FORCE_COMPUTE_HASH=0 + +(("$#")) || fail "Expect PUBLISHER and NAME" + +while (("$#")); do + case "$1" in + -h | --help) + # shellcheck disable=SC2028 # False positive, see https://github.com/koalaman/shellcheck/issues/2486 + echo \ + "Usage: nix-prefetch-openvsx [OPTIONS] PUBLISHER NAME VERSION=latest + +Fetch the vscode extension from Open VSX Registry +and print the registryRef (previously \"mktplcRef\") attributes in JSON format + +Options: + -a, --add-to-store Add the downloaded VSIX file to the Nix store. + This inherently requires downloading the VSIX file + even when hash querying is possible. + --base16 Print the hash in base-16 format. + --base32 Print the hash in base-32 format. + --base64 Print the hash in base-64 format. + -c, --compact Use compact JSON output instead of multi-line output. + --force-query Force nix-prefetch-openvsx to query the hash + --force-compute Force nix-prefetch-openvsx to download the VSIX file + and compute the hash. + -h, --help Print this help and exit. + -H, --no-hash Do not print the nix-hash value of this extension. + This prevents downloading the VSIX file when hash-querying + is not possible. + -M, --no-meta Do not print the meta attributes of this extension. + This speed up the program + by preventing the manifest downloading / unpacking. + -p, --platform PLATFORM Specify the targetPlatform of the extension. + -P, --no-print-platform Don't print the targetPlatform of the extension. + --print-configuration Print the configuration options of the extension. + --sri Print the hash in SRI format. + This is the default behavior + -t, --tmpdir TMPDIR Specify the directory + to create temporary directories in. + Default to \`\"\${TMPDIR:-/tmp}\"\` by \`mktemp\` + from the environment. + --type HASH_ALGO Specify the hash algorithm. + Default to sha256 + -u, --domain DOMAIN Specify the domain of the Open VSX Registry instance. + Default to + $DOMAIN + -U, --no-print-domain Don't print the domain of the Open VSX Registry instance. + +If non-flag arguments (PUBLISHER, NAME, VERSION, etc.) happen to start with '-', +they should be escaped with a '\\' prefix or be added after '--'." + exit 0 + ;; + -a | --add-to-store) + ADD_TO_STORE=1 + shift + ;; + --base16) + HASH_FORMAT="base16" + shift + ;; + --base32) + HASH_FORMAT="base32" + shift + ;; + --base64) + HASH_FORMAT="base64" + shift + ;; + -c | --compact) + COMPACT=1 + shift + ;; + --force-query) + FORCE_QUERY_HASH=1 + shift + ;; + --force-compute) + FORCE_COMPUTE_HASH=1 + shift + ;; + -H | --no-hash) + NO_HASH=1 + shift + ;; + -M | --no-meta) + NO_META=1 + shift + ;; + -p | --platform) + (("$#" >= 2)) || fail "Expect $1 PLATFORM" + VSIX_PLATFORM="$2" + shift 2 + ;; + -P | --no-print-platform) + NO_PRINT_PLATFORM=1 + shift + ;; + --print-configuration) + PRINT_CONFIGURATION=1 + shift + ;; + --sri) + HASH_FORMAT="sri" + shift + ;; + -t | --tmpdir) + (("$#" >= 2)) || fail "Expect $1 TMPDIR" + export TMPDIR="$2" + shift 2 + ;; + --type) + (("$#" >= 2)) || fail "Expect $1 HASH_ALGO." + HASH_ALGO="$2" + shift 2 + ;; + -u | --domain) + (("$#" >= 2)) || fail "Expect $1 DOMAIN" + DOMAIN="$2" + shift 2 + ;; + -U | --no-print-domain) + NO_PRINT_DOMAIN=1 + shift + ;; + -) + fail "Unexpected argument $1" + ;; + --) + shift + NONFLAG_ARGS+=("$@") + set -- + ;; + --?*) + fail "Unexpected argument $1" + ;; + -?*) + manage_shorthands + ;; + \\-*) + # If an argument begins with '\-', + # strip the prefixing backslash + # and put into NONFLAG_ARGS + NONFLAG_ARGS+=("${1:1}") + shift + ;; + *) + NONFLAG_ARGS+=("$1") + shift + ;; + esac +done + +if [[ "${#NONFLAG_ARGS[@]}" -lt 2 ]]; then + fail "Expect PUBLISHER and NAME" +fi +VSIX_PUBLISHER="${NONFLAG_ARGS[0]}" +VSIX_NAME="${NONFLAG_ARGS[1]}" +if [[ "${#NONFLAG_ARGS[@]}" -lt 3 || -z "${NONFLAG_ARGS[2]}" ]]; then + VSIX_VERSION_SPECIFIED="latest" +else + VSIX_VERSION_SPECIFIED="${NONFLAG_ARGS[2]}" +fi + +if ((FORCE_QUERY_HASH)) && ((FORCE_COMPUTE_HASH)); then + fail "--force-query and --force-compute must not coexist." +fi + +create_exttmp +trap cleanup EXIT + +if ((ADD_TO_STORE)); then + add_vsix_to_store +fi + +print_output diff --git a/pkgs/applications/editors/vscode/extension-registries/vscode-marketplace/nix-prefetch-vscode-marketplace/default.nix b/pkgs/applications/editors/vscode/extension-registries/vscode-marketplace/nix-prefetch-vscode-marketplace/default.nix new file mode 100644 index 0000000000000..2dfb54b9b0982 --- /dev/null +++ b/pkgs/applications/editors/vscode/extension-registries/vscode-marketplace/nix-prefetch-vscode-marketplace/default.nix @@ -0,0 +1,74 @@ +{ stdenvNoCC +, lib +, makeWrapper +, shellcheck +, bash +, coreutils +, curl +, jq +, unzip +, nix +, nix-prefetch-vsix-lib +}: + +stdenvNoCC.mkDerivation rec { + pname = "nix-prefetch-vscode-marketplace"; + version = "0.1.0"; + + preferLocalBuild = true; + + src = ./nix-prefetch-vscode-marketplace; + + dontUnpack = true; + + nativeBuildInputs = [ + makeWrapper + ]; + + buildInputs = [ + bash + ]; + + installPhase = '' + runHook preInstall + mkdir -p $out/bin + cp "$src" "$out/bin/nix-prefetch-vscode-marketplace" + chmod +x "$out/bin/nix-prefetch-vscode-marketplace" + patchShebangs --host "$out/bin/nix-prefetch-vscode-marketplace" + runHook postInstall + ''; + + postFixup = '' + wrapProgram "$out/bin/nix-prefetch-vscode-marketplace" \ + --prefix PATH : "${lib.makeBinPath [ + coreutils + curl + jq + unzip + nix + nix-prefetch-vsix-lib + ]}" + ''; + + doInstallCheck = true; + + installCheckInputs = [ + shellcheck + nix-prefetch-vsix-lib + ]; + + installCheckPhase = '' + runHook preInstallCheck + while IFS= read -r -d "" file; do + shellcheck -x -P "$PATH" "$file" + done < <(find "$out/bin" -mindepth 1 -type f,l -print0) + runHook postInstallCheck + ''; + + meta = with lib; { + description = "Prefetch vscode extensions from the official marketplace"; + license = licenses.mit; + platforms = platforms.all; + maintainers = with maintainers; [ ShamrockLee ]; + }; +} diff --git a/pkgs/applications/editors/vscode/extension-registries/vscode-marketplace/nix-prefetch-vscode-marketplace/nix-prefetch-vscode-marketplace b/pkgs/applications/editors/vscode/extension-registries/vscode-marketplace/nix-prefetch-vscode-marketplace/nix-prefetch-vscode-marketplace new file mode 100755 index 0000000000000..9cb571522d182 --- /dev/null +++ b/pkgs/applications/editors/vscode/extension-registries/vscode-marketplace/nix-prefetch-vscode-marketplace/nix-prefetch-vscode-marketplace @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +# Adapt from get_vsixpkg() of the original update_install_exts.sh + +set -eu -o pipefail + +declare -a NONFLAG_ARGS=() + +source nix-prefetch-vsix-lib.sh + +function prepare_vsix_url { + prepare_vsix_publisher + prepare_vsix_name + prepare_vsix_version_specified + VSIX_URL="https://$VSIX_PUBLISHER.gallery.vsassets.io/_apis/public/gallery/publisher/$VSIX_PUBLISHER/extension/$VSIX_NAME/$VSIX_VERSION_SPECIFIED/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage" +} + +VSIX_PUBLISHER="" +VSIX_NAME="" +VSIX_VERSION="" +VSIX_VERSION_SPECIFIED="" +VSIX_VERSION_FETCHED="" +COMPACT=0 +ADD_TO_STORE=0 +NO_HASH=0 +NO_META=0 +PRINT_CONFIGURATION=0 + +(("$#")) || fail "Expect PUBLISHER and NAME" + +while (("$#")); do + case "$1" in + -h | --help) + # shellcheck disable=SC2028 # False positive, see https://github.com/koalaman/shellcheck/issues/2486 + echo \ + "Usage: nix-prefetch-vscode-marketplace [OPTIONS] PUBLISHER NAME VERSION=latest + +Fetch the vscode extension from the official marketplace +and print the registryRef (previously \"mktplcRef\") attributes in JSON format + +Note: +The VSCode Extension Gallery API is left undocumented, +and we could only get the version, the hash, and the manifest (package.json) +by downloading the VSIX file directly. + +Options: + -a, --add-to-store Add the downloaded VSIX file to the Nix store. + --base16 Print the hash in base-16 format. + --base32 Print the hash in base-32 format. + --base64 Print the hash in base-64 format. + -c, --compact Use compact JSON output instead of multi-line output. + -h, --help Print this help and exit. + -H, --no-hash Do not print the nix-hash value of this extension. + -M, --no-meta Do not print the meta attributes of this extension. + --print-configuration Print the configuration options of the extension. + --sri Print the hash in SRI format. + This is the default behavior + -t, --tmpdir TMPDIR Specify the directory + to create temporary directories in. + Default to \`\"\${TMPDIR:-/tmp}\"\` by \`mktemp\` + from the environment. + --type HASH_ALGO Specify the hash algorithm. + Default to sha256 + +If non-flag arguments (PUBLISHER, NAME, VERSION, etc.) happen to start with '-', +they should be escaped with a '\\' prefix or be added after '--'." + exit 0 + ;; + -a | --add-to-store) + ADD_TO_STORE=1 + shift + ;; + --base16) + HASH_FORMAT="base16" + shift + ;; + --base32) + HASH_FORMAT="base32" + shift + ;; + --base64) + HASH_FORMAT="base64" + shift + ;; + -c | --compact) + COMPACT=1 + shift + ;; + -H | --no-hash) + NO_HASH=1 + shift + ;; + -M | --no-meta) + NO_META=1 + shift + ;; + --print-configuration) + PRINT_CONFIGURATION=1 + shift + ;; + --sri) + HASH_FORMAT="sri" + shift + ;; + -t | --tmpdir) + (("$#" >= 2)) || fail "Expect $1 TMPDIR" + export TMPDIR="$2" + shift 2 + ;; + --type) + (("$#" >= 2)) || fail "Expect $1 HASH_ALGO." + HASH_ALGO="$2" + shift 2 + ;; + -) + fail "Unexpected argument $1" + ;; + --) + shift + NONFLAG_ARGS+=("$@") + set -- + ;; + --?*) + fail "Unexpected argument $1" + ;; + -?*) + manage_shorthands + ;; + \\-*) + # If an argument begins with '\-', + # strip the prefixing backslash + # and put into NONFLAG_ARGS + NONFLAG_ARGS+=("${1:1}") + shift + ;; + *) + NONFLAG_ARGS+=("$1") + shift + ;; + esac +done + +if [[ "${#NONFLAG_ARGS[@]}" -lt 2 ]]; then + fail "Expect PUBLISHER and NAME" +fi +VSIX_PUBLISHER="${NONFLAG_ARGS[0]}" +VSIX_NAME="${NONFLAG_ARGS[1]}" +if [[ "${#NONFLAG_ARGS[@]}" -lt 3 || -z "${NONFLAG_ARGS[2]}" ]]; then + VSIX_VERSION_SPECIFIED="latest" +else + VSIX_VERSION_SPECIFIED="${NONFLAG_ARGS[2]}" +fi + +create_exttmp +trap cleanup EXIT + +if ((ADD_TO_STORE)); then + add_vsix_to_store +fi + +print_output diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 36577abfc3cd7..4cfb23c3975f9 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -38315,6 +38315,12 @@ with pkgs; nix-prefetch-github = with python3Packages; toPythonApplication nix-prefetch-github; + nix-prefetch-openvsx = callPackage ../applications/editors/vscode/extension-registries/openvsx/nix-prefetch-openvsx { }; + + nix-prefetch-vscode-marketplace = callPackage ../applications/editors/vscode/extension-registries/vscode-marketplace/nix-prefetch-vscode-marketplace { }; + + nix-prefetch-vsix-lib = callPackage ../applications/editors/vscode/extension-registries/commons/nix-prefetch-vsix-lib { }; + inherit (callPackages ../tools/package-management/nix-prefetch-scripts { }) nix-prefetch-bzr nix-prefetch-cvs