Skip to content

igorlogius/meta-addon-builder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

51 Commits
 
 

Repository files navigation

#!/usr/bin/env bash

# https://addons-server.readthedocs.io/en/latest/topics/api/v4_frozen/signing.html#v4-upload-version

#
# consts
# 

BASE_URL="https://addons.mozilla.org"
AMO_API_URL="$BASE_URL/api/v4/addons"

#
# functions
# 

# Array to hold the names of created temporary files and directories
temp_items=()

# Function to create a temporary file or directory
create_temp() {
    local temp_item
    local is_directory=false

    # Check for the directory switch
    if [[ "$1" == "--dir" ]]; then
        is_directory=true
        shift  # Remove the switch from the arguments
    fi

    if $is_directory; then
        temp_item=$(mktemp -d)  # Create a temporary directory
    else
        temp_item=$(mktemp)  # Create a temporary file
    fi

    if [[ $? -eq 0 ]]; then
        temp_items+=("$temp_item")  # Add the temp item to the array
        echo "$temp_item"  # Return the name of the temp item
    else
        echo "Failed to create temporary item" >&2
        return 1
    fi
}

# Function to clean up all created temporary files and directories
cleanup_temp_items() {
    for item in "${temp_items[@]}"; do
        if [[ -e "$item" ]]; then
            rm -rf "$item"  # Remove the temporary item
            echo "Removed temporary item: $item"
        fi
    done
    temp_items=()  # Clear the array after cleanup
}

# Trap to ensure cleanup on exit
trap cleanup_temp_items EXIT

# Function to calc the next version to upload to AMO
get_next_version() {
    local CURRENT_AMO_VERSION="$1" 
    local CURRENT_AMO_VERSION_PARTS=($(echo -n "$CURRENT_AMO_VERSION" | tr '.' ' ' ))
    local LAST_PART="${CURRENT_AMO_VERSION_PARTS[2]}"
    local NEW_LAST_PART=$((LAST_PART+1))
    echo "${CURRENT_AMO_VERSION_PARTS[0]}.${CURRENT_AMO_VERSION_PARTS[1]}.${NEW_LAST_PART}"
}

# Function to download the content of a remote 
get_remote_content() {
    local url="$1"
    local outfile=$(create_temp);
    local status=$(curl -s -L --url "$url" -w "%{http_code}" -o $outfile)
    if [ $status -ne 200 ];then
        echo ""
        cat $outfile 
        echo ""
        echo "ERROR: failed to download $url got status: $status"
        exit 1
    fi
    cat $outfile 
}

# Function to base64url encode ... without padding ===
b64url_encode() { 
    cat /dev/stdin | base64 -w0 | tr -d '=' | tr '/+' '_-'
}

# function to generate the jwt auth token 
generate_auth_token() {
    local header=$(echo -n '{"alg":"HS256","typ":"JWT"}' | b64url_encode)
    local iat=$(date +%s)
    local jti=$((iat+RANDOM))
    local exp=$((iat+30))
    local payload=$(echo -n "{\"iss\":\"${ISSUER}\",\"iat\":${iat},\"jti\":\"${jti}\",\"exp\":$exp}" | b64url_encode)
    local header_payload=$(echo -n "${header}.${payload}")
    local signature=$(echo -n "${header_payload}" | openssl dgst -binary -sha256 -hmac "${SECRET}" | b64url_encode)
    echo "${header_payload}.${signature}"
}

# function to upload the generated xpi 
upload_xpi(){
    local guid="$1"
    local version="$2"
    local xpi="$3"
    local outfile=$(create_temp)

    # note -g --globoff allows curly braces which are part of the GUID in the URL
    local status=$(curl -s -L --url "$AMO_API_URL/$guid/versions/$version/" \
        -g \
        -X PUT \
        --form "upload=@$xpi" \
        -H "Authorization: JWT $(generate_auth_token)" \
        -w "%{http_code}" \
        -o $outfile
    )
    if [ $status -ne 202 ];then
        echo ""
        cat $outfile 
        echo ""
        echo "ERROR: addon not accepted with status: $status"
        exit 1
    fi
}

check_deps(){
    MISSING=0
    for req in "$@";do
        type $req >/dev/null 2>&1 || { echo >&2 "ERROR: missing dependency: $req"; MISSING=1; }
    done
    [ $MISSING -ne 0 ] && { echo "ERROR: aborting missing dependencies"; exit 1; }
}

check_vars(){
    MISSING=0
    for req in "$@";do
        [ -z $(eval echo "\${${req}+x}") ] && { echo "ERROR: missing variable: $req"; MISSING=1; }
    done
    [ $MISSING -ne 0 ] && { echo "ERROR: aborting missing dependencies"; exit 1; }
}

update_version() {
    local fp="$1"
    local ver="$2"

    if [ ! -f $fp ];then
        echo "ERROR: no manifest.json"
        exit 1
    fi

    # update manifest.json with new version
    local tmpfile=$(create_temp)
    cat $fp > $tmpfile
    jq --arg VERSION "$ver" '.version = $VERSION' $tmpfile > $fp
}

update_AMO_info()  {
    local summary=$(cat ./manifest.json | jq -r '.description')
    local description=$(< ./README.md)
    local support_url="https://github.com/igorlogius/webextensions/issues/new/choose?title=$GITHUB_WORKFLOW%20-"
    local template='{
        "summary": { 
            "en-US": "" 
        },
        "description": { 
            "en-US": "" 
        },
        "support_url": {
            "en-US": ""
        }
    }'

    local updated_json=$(echo "$template" | jq --arg desciption "$description" --arg summary "$summary" --arg support_url "$support_url" '.description."en-US" = $desciption | .summary."en-US" = $summary | .support_url."en-US" = $support_url' )



    # note: we need to use the v5 API for this 
    # https://mozilla.github.io/addons-server/topics/api/addons.html#edit
    # This endpoint allows an add-on’s AMO metadata to be edited.
    local outfile=$(create_temp);
    local status=$(curl \
        -s \
        -L  \
        --url "$BASE_URL/api/v5/addons/addon/$GITHUB_WORKFLOW/"  \
        -X PATCH \
        -H 'Content-type: application/json' \
        -H "Authorization: JWT $(generate_auth_token)"  \
        -d "$updated_json" \
        -w "%{http_code}" \
        -o $outfile
    )
    if [ $status -ne 200 ];then
        echo ""
        cat $outfile 
        echo ""
        echo "ERROR: failed to update AMO infos $url got status: $status"
        exit 1
    fi
}


##
##  MAIN 
##

# testing 
#GITHUB_REPOSITORY_OWNER=igorlogius
#GITHUB_WORKFLOW=copy-tabs

check_vars GITHUB_REPOSITORY_OWNER GITHUB_WORKFLOW 
check_deps stat cat mktemp date zip curl jq 

# create and move to empty workdir 
WORKDIR=$(create_temp --dir)
cd $WORKDIR;

# get VERSION + GUID 
IFS=' ' read -r AMO_VERSION AMO_GUID <<<$(get_remote_content "$AMO_API_URL/addon/$GITHUB_WORKFLOW" | jq -r '.current_version.version, .guid' | tr '\n' ' ')

# determine next version
NEXT_AMO_VERSION=$(get_next_version "$AMO_VERSION")

# bulk download via curl -K with jq format magic also excludes README.md
curl -s -K <(get_remote_content "https://api.github.com/repos/$GITHUB_REPOSITORY_OWNER/webextensions/contents/sources/$GITHUB_WORKFLOW" | jq -r '.[] | select ( .type == "file") | "url = \(.download_url)\noutput = \(.name)"')

# replace module symlinks with real content
for file in *;do
    SIZE=$( stat -c "%s" "$file" )
    if [ $SIZE -gt 14 ]; then
        TMP=$( head -c 14 "$file" | tr -d '\0' )
        if [ "$TMP" = "../../modules/" ]; then
            get_remote_content "https://raw.githubusercontent.com/$GITHUB_REPOSITORY_OWNER/webextensions/main/modules/$file" > $file
        fi
    fi
done

# write new version to file
update_version "manifest.json" "${NEXT_AMO_VERSION}"

# pack files
XPIFILE="${GITHUB_WORKFLOW}-${NEXT_AMO_VERSION}-$(date '+%F_%H-%M').zip" 
zip -r "$XPIFILE" *


# # # 

check_vars ISSUER SECRET 
check_deps base64 openssl

upload_xpi  "$AMO_GUID"  "$NEXT_AMO_VERSION"  "$XPIFILE" 

# we need to use the v5 API for this 
update_AMO_info

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks