publish-auto-update.pl
1 #!/usr/bin/env perl 2 3 use strict; 4 use warnings; 5 use POSIX; 6 use Getopt::Std; 7 use File::Slurp; 8 9 sub usage() { 10 die("usage: $0 [-p api-port] dev-private-key [short-commit-hash]\n"); 11 } 12 13 my %opt; 14 getopts('p:', \%opt); 15 16 usage() if @ARGV < 1 || @ARGV > 2; 17 18 my $port = $opt{p} || 10391; 19 my $privkey = shift @ARGV; 20 my $commit_hash = shift @ARGV; 21 22 my $git_dir = `git rev-parse --show-toplevel`; 23 die("Cannot determine git top level dir\n") unless $git_dir; 24 25 chomp $git_dir; 26 chdir($git_dir) || die("Can't change directory to $git_dir: $!\n"); 27 28 open(POM, '<', 'pom.xml') || die ("Can't open 'pom.xml': $!\n"); 29 my $project; 30 while (<POM>) { 31 if (m/<artifactId>(\w+)<.artifactId>/o) { 32 $project = $1; 33 last; 34 } 35 } 36 close(POM); 37 38 my $apikey = read_file('apikey.txt'); 39 40 # Do we need to determine commit hash? 41 unless ($commit_hash) { 42 # determine git branch 43 my $branch_name = ` git symbolic-ref -q HEAD `; 44 chomp $branch_name; 45 $branch_name =~ s|^refs/heads/||; # ${branch_name##refs/heads/} 46 47 # short-form commit hash on base branch (non-auto-update) 48 $commit_hash ||= `git show --no-patch --format=%h`; 49 die("Can't find commit hash\n") if ! defined $commit_hash; 50 chomp $commit_hash; 51 printf "Commit hash on '%s' branch: %s\n", $branch_name, $commit_hash; 52 } else { 53 printf "Using given commit hash: %s\n", $commit_hash; 54 } 55 56 # build timestamp / commit timestamp on base branch 57 my $timestamp = `git show --no-patch --format=%ct ${commit_hash}`; 58 die("Can't determine commit timestamp\n") if ! defined $timestamp; 59 $timestamp *= 1000; # Convert to milliseconds 60 61 # locate sha256 utility 62 my $SHA256 = `which sha256sum || which sha256`; 63 chomp $SHA256; 64 die("Can't find sha256sum or sha256\n") unless length($SHA256) > 0; 65 66 # SHA256 of actual update file 67 my $sha256 = `git show auto-update-${commit_hash}:${project}.update | ${SHA256} | head -c 64`; 68 die("Can't calculate SHA256 of ${project}.update\n") unless $sha256 =~ m/(\S{64})/; 69 chomp $sha256; 70 71 # long-form commit hash of HEAD on auto-update branch 72 #my $update_hash = `git rev-parse refs/heads/auto-update-${commit_hash}`; 73 my $update_hash = `git rev-parse origin/auto-update-${commit_hash}`; 74 die("Can't find commit hash for HEAD on auto-update-${commit_hash} branch\n") if ! defined $update_hash; 75 chomp $update_hash; 76 77 printf "Build timestamp (ms): %d / 0x%016x\n", $timestamp, $timestamp; 78 printf "Auto-update commit hash: %s\n", $update_hash; 79 printf "SHA256 of ${project}.update: %s\n", $sha256; 80 81 my $tx_type = 10; 82 my $tx_timestamp = time() * 1000; 83 my $tx_group_id = 1; 84 my $service = 1; 85 printf "\nARBITRARY(%d) transaction with timestamp %d, txGroupID %d and service %d\n", $tx_type, $tx_timestamp, $tx_group_id, $service; 86 87 my $data_hex = sprintf "%016x%s%s", $timestamp, $update_hash, $sha256; 88 printf "\nARBITRARY transaction data payload: %s\n", $data_hex; 89 90 my $n_payments = 0; 91 my $data_type = 1; # RAW_DATA 92 my $data_length = length($data_hex) / 2; # two hex chars per byte 93 my $fee = 0.01 * 1e8; 94 my $nonce = 0; 95 my $name_length = 0; 96 my $identifier_length = 0; 97 my $method = 0; # PUT 98 my $secret_length = 0; 99 my $compression = 0; # None 100 my $metadata_hash_length = 0; 101 102 die("Something's wrong: data length is not 60 bytes!\n") if $data_length != 60; 103 104 my $pubkey = `curl --silent --url http://localhost:${port}/utils/publickey --data ${privkey}`; 105 die("Can't convert private key to public key:\n$pubkey\n") unless $pubkey =~ m/^\w{44}$/; 106 printf "\nPublic key: %s\n", $pubkey; 107 108 my $pubkey_hex = `curl --silent --url http://localhost:${port}/utils/frombase58 --data ${pubkey}`; 109 die("Can't convert base58 public key to hex:\n$pubkey_hex\n") unless $pubkey_hex =~ m/^[A-Za-z0-9]{64}$/; 110 printf "Public key hex: %s\n", $pubkey_hex; 111 112 my $address = `curl --silent --url http://localhost:${port}/addresses/convert/${pubkey}`; 113 die("Can't convert base58 public key to address:\n$address\n") unless $address =~ m/^\w{33,34}$/; 114 printf "Address: %s\n", $address; 115 116 my $reference = `curl --silent --url http://localhost:${port}/addresses/lastreference/${address}`; 117 die("Can't fetch last reference for $address:\n$reference\n") unless $reference =~ m/^\w{87,88}$/; 118 printf "Last reference: %s\n", $reference; 119 120 my $reference_hex = `curl --silent --url http://localhost:${port}/utils/frombase58 --data ${reference}`; 121 die("Can't convert base58 reference to hex:\n$reference_hex\n") unless $reference_hex =~ m/^[A-Za-z0-9]{128}$/; 122 printf "Last reference hex: %s\n", $reference_hex; 123 124 my $raw_tx_hex = sprintf("%08x%016x%08x%s%s%08x%08x%08x%08x%08x%08x%08x%08x%02x%08x%s%08x%08x%016x", $tx_type, $tx_timestamp, $tx_group_id, $reference_hex, $pubkey_hex, $nonce, $name_length, $identifier_length, $method, $secret_length, $compression, $n_payments, $service, $data_type, $data_length, $data_hex, $data_length, $metadata_hash_length, $fee); 125 printf "\nRaw transaction hex:\n%s\n", $raw_tx_hex; 126 127 my $raw_tx = `curl --silent --url http://localhost:${port}/utils/tobase58/${raw_tx_hex}`; 128 die("Can't convert raw transaction hex to base58:\n$raw_tx\n") unless $raw_tx =~ m/^\w{300,320}$/; # Roughly 305 to 320 base58 chars 129 printf "\nRaw transaction (base58):\n%s\n", $raw_tx; 130 131 my $sign_data = qq|' { "privateKey": "${privkey}", "transactionBytes": "${raw_tx}" } '|; 132 my $signed_tx = `curl --silent -H "accept: text/plain" -H "Content-Type: application/json" --url http://localhost:${port}/transactions/sign --data ${sign_data}`; 133 die("Can't sign raw transaction:\n$signed_tx\n") unless $signed_tx =~ m/^\w{390,410}$/; # +90ish longer than $raw_tx 134 printf "\nSigned transaction:\n%s\n", $signed_tx; 135 136 # Get the origin URL - So that we will be able to TEST the obtaining of the forknet.update... 137 my $origin = `git remote get-url origin`; 138 chomp $origin; # Remove any trailing newlines 139 die("Unable to get github url for 'origin'?\n") unless $origin; 140 141 # Debug: Print the origin URL 142 print "Full Origin URL: $origin\n"; 143 144 # Extract the repository path (e.g., Forknet/forknet) NOTE - github is case-sensitive with repo names 145 my $repo; 146 if ($origin =~ m/[:\/]([\w\-]+\/[\w\-]+)\.git$/) { 147 $repo = $1; 148 print "Extracted direct repository path: $repo\n"; 149 if ($repo =~ m/^forknet\//i) { 150 $repo =~ s/^forknet\//Forknet\//; 151 print "Corrected repository path capitalization: $repo\n"; 152 } 153 print "Please verify the direct repository path. Current: '$repo'\n"; 154 print "If incorrect, input the correct direct repository path (e.g., 'Forknet/forknet' or 'bob/forknet').NOTE - github is CASE SENSITIVE for repository urls... Press Enter to keep the extracted version: "; 155 my $input = <STDIN>; 156 if ($input =~ m/^forknet\//i) { 157 $input =~ s/^forknet\//Forknet\//; 158 print "Corrected repository path capitalization: $repo\n"; 159 } 160 chomp $input; 161 $repo = $input if $input; # Update repo if user provides input 162 163 } else { 164 # Default to forknet/forknet if extraction fails 165 $repo = "Forknet/forknet"; 166 print "Failed to extract repository path from origin URL. Using default: $repo\n"; 167 168 # Prompt the user for confirmation or input 169 print "Please verify the repository path. Current: '$repo'\n"; 170 print "If incorrect, input the correct repository path (e.g., 'Forknet/forknet' or 'BobsCodeburgers/forknet'). NOTE - GitHub is CASE SENSITIVE for repository urls... Press Enter to keep the default: "; 171 my $input = <STDIN>; 172 if ($input =~ m/^forknet\//i) { 173 $input =~ s/^forknet\//Forknet\//; 174 print "Corrected repository path capitalization: $repo\n"; 175 } 176 chomp $input; 177 $repo = $input if $input; # Update repo if user provides input 178 } 179 180 # Debug: Print the final repository path 181 print "Final direct repository path: $repo\n"; 182 183 # Construct the update URL 184 my $update_url = "https://github.com/${repo}/raw/${update_hash}/${project}.update"; 185 print "Final update URL: $update_url\n"; 186 187 188 my $fetch_result = `curl --silent -o /dev/null --location --range 0-1 --head --write-out '%{http_code}' --url ${update_url}`; 189 die("\nUnable to fetch update from ${update_url}\n") if $fetch_result ne '200'; 190 printf "\nUpdate fetchable from ${update_url}\n"; 191 192 # Flush STDOUT after every output 193 $| = 1; 194 print "\n"; 195 for (my $delay = 5; $delay > 0; --$delay) { 196 printf "\rSubmitting transaction in %d second%s... CTRL-C to abort ", $delay, ($delay != 1 ? 's' : ''); 197 sleep 1; 198 } 199 200 printf "\rSubmitting transaction NOW... \n"; 201 my $result = `curl --silent --url http://localhost:${port}/transactions/process --data ${signed_tx}`; 202 chomp $result; 203 die("Transaction wasn't accepted:\n$result\n") unless $result eq 'true'; 204 205 my $decoded_tx = `curl --silent -H "Content-Type: application/json" --url http://localhost:${port}/transactions/decode --data ${signed_tx}`; 206 printf "\nTransaction accepted:\n$decoded_tx\n";