My friend Oliver approached me recently with an idea for a project that we could both work on together and after he explained the basic premise I was on board!
The initial concept was wouldn’t it be fun to have a bot that could publish your git commits for you? Which of course the only correct answer is “HELL YEAH!”!
Oliver, built a prototype and then had the great idea of using a Raspberry Pi to act as a sort of “Auto Commit Robot” and to test his newly minted bot he modified it to create randomly generated files to commit, and that’s when it hit us… There was a bigger story here…
One of the first uses for the bot we considered was the the idea that although most employers are reasonable and will look at the quality of your code, there are still some that look at how frequently you commit as a large indicator of how ‘driven’ or ‘voracious’ of a programmer you are, which if you think about it makes absolutely no sense at all!
So Oliver suggested, what if you were to just commit the random text he was generating to a private repo but have your commit data publicly available on your GitHub account, this would allow you to push commits every day (or at least on a schedule) and establish a history of regularly pushing commits which is exactly what he built! When he publishes his half of this project I will link to it here.
I highly recommend when it’s published that you head over to read his post and give it a like too! He’s also really nice so feel free to leave him a comment as well, I am sure he would appreciate it! 😉
Now this is where I come in. My portion of this project stems from the fact that not everyone has a Raspberry Pi (or Linux machine since for this purpose they are basically are one in the same) lying around and also not everyone pays GitHub to maintain private repos, so a lot of people are just using GitHub to store free public repositories.
So, my challenge is to turn his bot into a cross platform solution so that it also works with public repos because anyone looking past just the number of commits would discover the ruse of randomly generated data.
So I modified my scenario a little bit so that my challenge was clearer.
Consider the following: It’s the the new year and you are looking to “get a head start on commits”… or maybe you have a bet with a friend that you can make more commits to your GitHub than they can between now and next month… or maybe you just want to watch the Stock Market fall… Whatever the reason is you decide that you happen to need an auto commit bot…
Once I had a scenario I knew that in order to solve this challenge the data that Oliver’s bot was pushing to the repo had to pass a basic “visual test” ( if not a run test ) even if the results of the code was useless.
With that in mind I set to work designing my methodology.
-
- First, I make the following assumption… ANY line of code that is capable of passing an eval() MUST be good because it “runs” so if I can generate the code segments and make it look like real code (more or less) even if it essentially does nothing, well it’s that much harder to question the validity of the commit, isn’t it?
- Further, use shell_exec() to execute the git commands in a cross-platform way.
I could hypothetically generate unit tests using the ASSERT function (thinking in PHP here 😛 ) if I wanted to go that route, though I’ll leave that for you to add to your own implementation!
- In any case I set to work coding generate-functions-example.php
generate-functions-example.php
<?php
function RandomString($length) {
$output = '';
$alphabet = 'abcdefghijklmnopqrstuvwxyz';
$alphabet_length = strlen($alphabet);
for ($i = 0; $i < $length; $i++) {
$output .= $alphabet[mt_rand(0, $alphabet_length - 1)];
}
return $output;
}
function SelectLanguageFunction($param) {
$output = '';
$action = mt_rand(0, 38);
// abs
if($action == 0){
$output .= 'abs(' . $param . ')';
}
// acos
elseif($action == 1){
$output .= 'acos(' . $param . ')';
}
// acosh
elseif($action == 2){
$output .= 'acosh(' . $param . ')';
}
// asin
elseif($action == 3){
$output .= 'asin(' . $param . ')';
}
// asinh
elseif($action == 4){
$output .= 'asinh(' . $param . ')';
}
// atan
elseif($action == 5){
$output .= 'atan(' . $param. ')';
}
// atan2
elseif($action == 6){
$output .= 'atan2(' . $param . ', ' . "($param + " . mt_rand(3, 7) . ') * pi())';
}
// atan
elseif($action == 7){
$output .= 'atan(' . $param . ')';
}
// atanh
elseif($action == 8){
$output .= 'atanh(' . $param . ')';
}
// bindec
elseif($action == 9){
$output .= 'bindec(' . $param . ')';
}
// ceil
elseif($action == 10){
$output .= 'ceil(' . $param . ')';
}
// cos
elseif($action == 11){
$output .= 'cos(' . $param . ')';
}
// cosh
elseif($action == 12){
$output .= 'cosh(' . $param . ')';
}
// decbin
elseif($action == 13){
$output .= 'decbin(' . $param . ')';
}
// dechex
elseif($action == 14){
$output .= 'dechex(' . $param . ')';
}
// decoct
elseif($action == 15){
$output .= 'decoct(' . $param . ')';
}
// deg2rad
elseif($action == 16){
$output .= 'decoct(' . $param . ')';
}
// exp
elseif($action == 17){
$output .= 'exp(' . $param . ')';
}
// expm1
elseif($action == 18){
$output .= 'expm1(' . $param . ')';
}
// floor
elseif($action == 19){
$output .= 'floor(' . $param . ')';
}
// fmod
elseif($action == 20){
$output .= 'fmod(' . $param . ', 0)';
}
// hexdec
elseif($action == 21){
$output .= 'hexdec(' . $param . ')';
}
// hypot
elseif($action == 22){
$output .= 'hypot(' . $param . ', ' . $param . ' * 2)';
}
// is_finite
elseif($action == 23){
$output .= 'is_finite(' . $param . ')';
}
// is_infinite
elseif($action == 24){
$output .= 'is_infinite(' . $param . ')';
}
// is_nan
elseif($action == 25){
$output .= 'is_nan(' . $param . ')';
}
// log10
elseif($action == 26){
$output .= 'log10(' . $param . ')';
}
// log1p
elseif($action == 27){
$output .= 'log1p(' . $param . ')';
}
// log
elseif($action == 28){
$output .= 'log(' . $param . ')';
}
// max
elseif($action == 29){
$output .= 'max(' . $param . ')';
}
// min
elseif($action == 30){
$output .= 'min(' . $param . ')';
}
// octdec
elseif($action == 31){
$output .= 'octdec(' . $param . ')';
}
// pow
elseif($action == 32){
$output .= 'pow(' . $param . ',' . mt_rand(2, 7) . ')';
}
// rad2deg
elseif($action == 33){
$output .= 'rad2deg(' . $param . ')';
}
// round
elseif($action == 34){
$output .= 'round(' . $param . ')';
}
// sin
elseif($action == 34){
$output .= 'sin(' . $param . ')';
}
// sinh
elseif($action == 35){
$output .= 'sinh(' . $param . ')';
}
// sqrt
elseif($action == 36){
$output .= 'sqrt(' . $param . ')';
}
// tan
elseif($action == 37){
$output .= 'tan(' . $param . ')';
}
// tanh
elseif($action == 38){
$output .= 'tanh(' . $param . ')';
}
return $output;
}
function SelectOpperation() {
$action = mt_rand(0, 5);
// Addition +
if($action == 0){
return '+';
}
// Subtraction -
elseif($action == 1){
return '-';
}
// Multiplication *
elseif($action == 2){
return '*';
}
// Division /
elseif($action == 3){
return '/';
}
// Modulus %
elseif($action == 4){
return '%';
}
// Exponentiation **
elseif($action == 5){
return '**';
}
}
function GenerateRandomFunction() {
// name the function
$function_name = ucfirst(RandomString(mt_rand(2, 7)));
// how many parameters should the function take?
$number_of_parameters = mt_rand(1, 3);
$parameters = array();
for ($i = 0; $i < $number_of_parameters; $i++) {
$param_name = RandomString(mt_rand(2, 5));
$parameters[] = "$$param_name";
}
// build function comment
$funk_header = '/**' . PHP_EOL; // open comment
$funk_header .= ' * Does Something' . PHP_EOL; // function description
$funk_header .= ' *' . PHP_EOL;
foreach($parameters as $parameter){
$funk_header .= ' * @param ' . $parameter . ' Add Description' . PHP_EOL; // parameter description
}
$funk_header .= ' *' . PHP_EOL;
$funk_header .= ' */' . PHP_EOL; // close comment
// build function
$funk = 'function ' . $function_name . '(' . implode(', ', $parameters) . '){ ' . PHP_EOL;
// do something with the parameters
$funk .= str_repeat(' ', 4) . 'return ';
foreach( $parameters as $key=>$parameter){
$funk .= SelectLanguageFunction($parameter); // do something with the parameters
if($key < $number_of_parameters - 1){
$funk .= ' '. SelectOpperation() .' '; // add opperation
}
}
$funk .= ';'; // add semicolon
// build end of function
$funk .= PHP_EOL . '}' . PHP_EOL . PHP_EOL;
return $funk_header . $funk;
}
/////////////////////////////////////////////
$number_of_files = 10; // how many files to generate
// create the files
for($f = 1; $f <= $number_of_files; $f++)
{
$file = fopen('functions/' . RandomString(mt_rand(3, 7)) . '.php','w');
fwrite($file, '<?php' . PHP_EOL);
for($i = 0; $i < mt_rand(3, 17); $i++){
fwrite($file, GenerateRandomFunction() . PHP_EOL);
}
fclose($file);
}
This code could be simplified by making use of arrays of language functions rather than hard coding them as if/else statements however I’ll leave that for you to implement if you want.
If you run this code the you will get 10 PHP files that contain a random number of functions (that will eval()) in a sub-folder called ‘functions’ and each function will have it’s own comment header that lists the parameters and included a place for a description of the function and the parameters.
At first glance the generated code appears complicated and useful, here is an example function generated:
/**
* Does Something
*
* @param $cl Add Description
* @param $jw Add Description
* @param $qobwa Add Description
*
*/
function Icbekz($cl, $jw, $qobwa){
return atan($cl) + hypot($jw, $jw * 2) ** atan2($qobwa, ($qobwa + 3) * pi());
}
You could use this function like this:
echo Icbekz(4.3, 7.1, 22);
With the resulting output being: 3.470139188433
Of course this works but the output is essentially useless except for exactly what we want it for, generating functional non-sense code!
At this point whatever you set the $number_of_files variable to is how many new files you will get in the functions folder every time the script is run.
Schedule this PHP script to run on a Linux machine using a Chron Job or on a Mac using a Scheduled Job or Windows using Scheduled Task so that it runs weekly, daily or even hourly depending on your needs.
Now all we need is a cross platform script to use GIT to upload the new files to your repo once the other script is complete.
update-repo.php
// !IMPORTANT! Manually Pull/Create your repo before using this script
// This script assumes that you keep your repos in your
// user folder in a folder called repos.
// Adjust the $local_path variable as needed
$repo_name = 'test';
$local_path = '$HOME'."/repos/$repo_name/"; // linux & mac path example
// $local_path = "%userprofile%\repos\$repo_name"; // win path example
////////////////////////////////////////////////////////////////////////////////////
/// Put anything you want to occur before the push here.
////////////////////////////////////////////////////////////////////////////////////
// comment this line after the script has run once
// it updates the config file for the repo to tell git to store the
// credentials after you enter them once
shell_exec("cd $local_path;git config credential.helper store");
// Pull Repo
shell_exec("cd $local_path;git pull");
// THE FIRST TIME THIS SCRIPT RUNS YOU WILL BE PROMPTED
// TO MANUALLY ENTER CREDENTIALS HERE.
// Add all new files
shell_exec("cd $local_path;git add *");
// Push new data
shell_exec("cd $local_path;git push origin master");
///////////////////////////////////////////////////////////////////////////////////
/// Put anything you want to occur after the push here.
///////////////////////////////////////////////////////////////////////////////////
Schedule this Script to run after the generator and all the newly generated code will be pushed to your GitHub repo on a regular interval! 😉
You can find this project on my GitHub account for free here: Auto Commit Bot
I have also created a version that will generate complete class files and you can see examples of the output on my GitHub in the classes subfolder for this project. All of my $1 or greater supporters on Patreon can download a copy of this project that includes the Class Generator.
Don’t forget to follow my up-to-date & ongoing research over on my public BookmarkNinja tab!
With that, everyone have a great day and I will see you all in my next post.
Much Love,
~Joy