diff --git a/DenseChangelog/LastUpdateFinder.java b/DenseChangelog/LastUpdateFinder.java new file mode 100644 index 0000000..c30cd1e --- /dev/null +++ b/DenseChangelog/LastUpdateFinder.java @@ -0,0 +1,39 @@ +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +@SuppressWarnings("UtilityClass") +public class LastUpdateFinder { + private static final File mLastUpdateFile = new File("last_update_time"); + + private LastUpdateFinder() { + } + + @SuppressWarnings("SimpleDateFormatWithoutLocale") + public static void main(String... args) throws JSONException, IOException { + if (args[0] == null) { + return; + } + + JSONArray input = new JSONObject(args[0]).getJSONArray("list"); + long time = Long.parseLong((String) input.getJSONObject(0).get("modified")) * 1000; + DateFormat df = new SimpleDateFormat("MM-dd-yyyy"); + FileWriter fileWriter = null; + try { + fileWriter = new FileWriter(mLastUpdateFile); + fileWriter.append(df.format(time)); + } finally { + if (fileWriter == null) { + throw new AssertionError(); + } + fileWriter.close(); + } + System.out.println("found date of last update: " + df.format(time)); + } +} diff --git a/DenseChangelog/ParseDenseChangelog.java b/DenseChangelog/ParseDenseChangelog.java new file mode 100644 index 0000000..9b76343 --- /dev/null +++ b/DenseChangelog/ParseDenseChangelog.java @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2012 The Android Open Kang Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Created with IntelliJ IDEA. + * Author: JBirdVegas + * Date: 10/31/12 + */ +public class ParseDenseChangelog { + static String TAG; + static boolean DEBUG = false; // always ship false else + // term output is mostly noise + // exit codes + static int NO_ERROR = 0; + static int FAILED_TO_READ_INPUT_FILE = 1; + static int FAILED_TO_MAKE_NEW_FILE = 2; + + static int DEFAULT_BUFFER_SIZE = 1048576; + static String LINE_RETURN = System.getProperty("line.separator"); + static File outFile; + + /** + * class to make JSON formatted changelog + * + * @param args args[0]= fileIn + * args[1]= fileOut (optional) + */ + public static void main(String[] args) { + if (args.length < 1) { + System.out.println("missing required params please provide input file path"); + return; + } + + if (true) { + for (String s : args) { + System.out.println("Found arg (" + s + ")"); + } + } + + printLines(2); + + // grab the class name + TAG = Thread.currentThread().getStackTrace()[1].getClassName(); + System.out.println("Hello welcome to " + TAG); + + File inFile = new File(args[0]); + + try { + if (!Character.isDigit(args[1].charAt(0))) { + outFile = new File(args[1]); + } else { + outFile = new File(inFile.getParentFile().getAbsolutePath() + + "/dense_" + inFile.getName()); + } + } catch (IndexOutOfBoundsException outOfBounds) { + outFile = new File(inFile.getParentFile().getAbsolutePath() + + "/dense_" + inFile.getName()); + } + if (!inFile.exists()) { + System.out.println("input file was not found... check your path"); + System.exit(FAILED_TO_READ_INPUT_FILE); + } + + System.out.println("Parsing input file: " + inFile.getAbsolutePath()); + + // read the file into a String + String jsonString = readFile(inFile); + if (jsonString == null) { + System.exit(FAILED_TO_READ_INPUT_FILE); + } + System.out.println("Log parser splicing at symbol: ¶"); + String[] commitsList = splitCommits(jsonString, "\\¶"); + if (DEBUG) { + for (String s : commitsList) { + System.out.println("commits to string: " + s); + } + } + int numberOfCommits = commitsList.length; + System.out.println("Generating JSON formated output..."); + JSONArray jsonArray = new JSONArray(); + for (int i = 0; numberOfCommits > i; i++) { + jsonArray.put(getJsonObjectFromCommit(commitsList[i])); + } + + if (args[args.length - 1] != null && args[args.length - 1].trim() != "") { + System.out.println("Removing indices outside of range"); + JSONObject commit = new JSONObject(); + JSONArray rangedList = new JSONArray(); + for (int i = 0; jsonArray.length() > i; i++) { + try { + commit = jsonArray.getJSONObject(i); + Date time = new Date(); + DateFormat df = new SimpleDateFormat("EEE MMM dd hh:mm:ss yyyy"); + DateFormat endFormat = new SimpleDateFormat("MM/dd/yyyy"); + Date endDate = endFormat.parse(args[args.length - 1]); + try { + time = df.parse(commit.getString("committer_date")); + } catch (NumberFormatException e) { + time = df.parse(commit.getString("author_date")); + } + + if (time.before(endDate)) { + rangedList.put(jsonArray.getJSONObject(i)); + if (DEBUG) System.out.println("found commit in acceptable range"); + } else { + if (DEBUG) System.out.println("commit not in range" + endDate.toString()); + } + } catch (JSONException e) { + if (DEBUG) e.printStackTrace(); + } catch (ParseException e) { + if (DEBUG) e.printStackTrace(); + } + } + try { + bufferedFileWriter(rangedList.toString(DEBUG ? 4 : 0)); + showSuccessMessage(); + } catch (JSONException je) { + je.printStackTrace(); + System.exit(FAILED_TO_MAKE_NEW_FILE); + } + } else { + System.out.println("args not acceptable: " + args[args.length - 1]); + } + + try { + bufferedFileWriter(jsonArray.toString(DEBUG ? 4 : 0)); + showSuccessMessage(); + } catch (JSONException js) { + js.printStackTrace(); + System.exit(FAILED_TO_MAKE_NEW_FILE); + } + } + + private static void printLines(int lines) { + for (int i = 0; lines > i; i++) { + System.out.print(LINE_RETURN); + } + } + + private static void bufferedFileWriter(String jsonText) { + if (outFile.exists()) + outFile.delete(); + + System.out.println("Writting file: " + outFile.getAbsolutePath()); + try { + BufferedWriter bufferedWriter = null; + try { + outFile.createNewFile(); + // buffer is large but we may be making large files + bufferedWriter = new BufferedWriter(new FileWriter(outFile), DEFAULT_BUFFER_SIZE); + bufferedWriter.write(jsonText); + } finally { + bufferedWriter.close(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + System.exit(FAILED_TO_MAKE_NEW_FILE); + } + } + + private static JSONObject getJsonObjectFromCommit(String commitString) { + JSONObject commitObject = new JSONObject(); + String[] info = splitCommits(commitString, "\\|"); + try { + commitObject.put("team_credit", info[0]); + commitObject.put("path", info[1]); + commitObject.put("commit_hash", info[2]); + commitObject.put("parent_hashes", info[3]); + commitObject.put("author_name", info[4]); + commitObject.put("author_date", info[5]); + commitObject.put("committer_name", info[6]); + commitObject.put("committer_date", info[7]); + commitObject.put("subject", info[8]); + commitObject.put("body", info[9]); + if (DEBUG) { + debugParse("team_credit", info[0]); + debugParse("path", info[1]); + debugParse("commit_hash", info[2]); + debugParse("parent_hashes", info[3]); + debugParse("author_name", info[4]); + debugParse("author_date", info[5]); + debugParse("committer_name", info[6]); + debugParse("committer_date", info[7]); + debugParse("subject", info[8]); + debugParse("body", info[9]); + } + } catch (JSONException e) { + // shouldn't happen + e.printStackTrace(); + } catch (IndexOutOfBoundsException lengthError) { + // our split array is the incorrect length + } + return commitObject; + } + + private static void debugParse(String name, String value) { + System.out.println(name + " : " + value); + } + + // read the file + private static String readFile(File inFile) { + // this bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4715154 + // prevents us from performing clean up here in java + // instead we must perform after generation via shell + String unformattedFileString = null; + try { + FileInputStream stream = new FileInputStream(inFile); + try { + FileChannel fc = stream.getChannel(); + MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); + unformattedFileString = Charset.defaultCharset().decode(bb).toString(); + } finally { + stream.close(); + } + } catch (IOException ioe) { + // bail we couldn't find the file + ioe.printStackTrace(); + System.exit(FAILED_TO_READ_INPUT_FILE); + } + return unformattedFileString; + } + + private static String[] splitCommits(String fileText, String regex) { + return fileText.split(regex); + } + + private static void showSuccessMessage() { + printLines(2); + System.out.println("Success! your changelog sir: " + + outFile.getAbsolutePath()); + System.exit(0); + } +} diff --git a/DenseChangelog/json-org.jar b/DenseChangelog/json-org.jar new file mode 100644 index 0000000..3e53d2e Binary files /dev/null and b/DenseChangelog/json-org.jar differ diff --git a/README b/README index e4b5b51..a8ba789 100644 --- a/README +++ b/README @@ -59,6 +59,26 @@ SO YOU WANT TO USE PSEUDO? Great! That's what she's here for! I do ask though, if you find any ways of making her better, shoot a pull request our way. +WHAT IS DENSE CHANGELOG? + + DenseChangelog is a command line java applet that parses git log --pretty + outputs into useful json output. This format is good for communicating + data to devices, to be parsed into a useful organization changelog + + REQUIREMENTS: to produce JSON formatted changelog you must ensure + pseudo_buildbot directory is present in $PATH; java/javac must be + propperly installed and the included json-org.jar from + http://www.json.org/ is expected in the same directory as + ParseDenseChangelog.java + + EXAMPLE: to generate JSON changelog just execute denseChangelog.sh from $TOP + denseChangelog.sh -v -f /path/to/output.json -d MM/dd/yyyy + *** all flags are optional *** + *** only use -d flag if custom begining date is required; probably better + to let the script find the update date itself, its good at that *** + *** -v (Verbose) flag executes 'set -x' *** + *** default outputs to $TOP/dense_denseChangelog_MM-dd-yyyy.json *** + PSEUDOS FUTURE -Add an if override for the .build_target file so user doesn't have to @@ -68,8 +88,9 @@ PSEUDOS FUTURE -Add a nightly/RC/stable toggle for the builds. - -And more! + -DenseChangelog is java; java code can always get better. + -And more! AUTHORS OF PSEUDO @@ -78,3 +99,6 @@ AUTHORS OF PSEUDO KhasMek (http://github.com/KhasMek) -Kanger of original script and maker of moar betterer. + + JBirdVegas (http://github.com/JBirdVegas) + -Java/JSON junkie and master Unicorn slayer diff --git a/denseChangelog.sh b/denseChangelog.sh new file mode 100755 index 0000000..d05ccde --- /dev/null +++ b/denseChangelog.sh @@ -0,0 +1,125 @@ +#!/bin/bash + +# Usage: +# From repo root ($TOP) +# denseChangelog -v -d 10/11/12 -f myOutFile.json +# Flags: +# -v = verbose +# -d = parse changelog from this date +# (if not supplied we will attempt to +# automatically find the last update date) +# *optional* +# -f = output file *optional* + +today=`date +"%m-%d-%Y"` +rdir=`pwd` + +# Find the parser +jsonParser="$(dirname "$(find . -type f -name denseChangelog.sh | head -1)")"/DenseChangelog + +# Usage help screen +usageText="\n\nAOKP dense changelog generator... generates the json formatted change log" +usageText+="\n\nThis software is licensed under the Apache License v2" +usageText+="\n for the full text look here: http://www.apache.org/licenses/LICENSE-2.0.txt" +usageText+="\n\nUsage denseChangelog.sh (-v) (-d MM-dd-yyyy) (-f /output/path/file.json)" +usageText+="\n\t*all flags are optional*" +usageText+="\n\n-v\t\t\t\tVerbose" +usageText+="\n-d MM-dd-yyyy\t\t\tGenerate changelog from date" +usageText+="\n-f /output/path/file.json\tPath to save dense formatted (json) changelog\n" +usageText+="\n-e GUI to select tags from platform_manifest to be used as date range\n" +# get all flags +while getopts vd:hf:e: args +do + case ${args} in + v) verbose=true;; + d) startDateRange=$OPTARG;; + f) fileout=$OPTARG;; + e) endDate=$OPTARG;; + # bail on invalid flags + h) echo -e ${usageText}; exit 0;; + ?) echo -e ${usageText}; exit 10;; + esac +done +echo "found endDate:" ${endDate} + +# if no date supplied then we look on goo.im for update date +if [ -z "${startDateRange}" ]; then + echo "No date supplied looking on Goo.Im for latest update date" + echo -e "To supply a custom date use flag -d MM/dd/yyyy\n" + cd ${jsonParser} + json=$(curl 'http://goo.im/json2&path=/devs/aokp/toro') + echo -e "\n" + javac -cp .:json-org.jar LastUpdateFinder.java + java -cp .:json-org.jar LastUpdateFinder ${json} + startDateRange=$(cat last_update_time) + rm LastUpdateFinder.class last_update_time + cd ${rdir} +fi + +# remove old changelogs +rm -q ${rdir}/denseChangelog_${today}.json > /dev/null 2>&1; +rm -q ${rdir}/jsonChangelog_${today}.json > /dev/null 2>&1; + +# Find the directories to log +find ${rdir} -name .git | sed 's/\/.git//g' | sed 'N;$!P;$!D;$d' | while read line +do + cd ${line} + # Test to see if the repo needs to have a changelog written. + log=$(git log --pretty="%H|%P|%an|%ad|%cn|%cd|%s|%B" --no-merges --since=${startDateRange} --date-order) + project=$(git remote -v | head -n1 | awk '{print $2}' | sed 's/.*\///' | sed 's/\.git//') + + if [ -z "${log}" ]; then + if [ ! -z "${verbose}" ]; then + echo "Nothing updated on ${project}, skipping" + fi + else + # Prepend group project ownership to each project. + origin=`grep "${project}" ${rdir}/.repo/manifest.xml | awk {'print $4'} | cut -f2 -d '"'` + if [ "${origin}" = "aokp" ]; then + proj_credit=AOKP + elif [ "${origin}" = "aosp" ]; then + proj_credit=AOSP + elif [ "${origin}" = "cm" ]; then + proj_credit=CyanogenMod + else + proj_credit="No Project Associated" + fi + + # Write the changelog + echo "¶${proj_credit}|${project}|" >> "${rdir}"/denseChangelog_${today}.json + echo "${log}" | while read line + do + echo "${line}" >> "${rdir}"/denseChangelog_${today}.json + done + echo "" >> "${rdir}"/denseChangelog_${today}.json + fi +done + +# move prompt to directory of our JSON parser +cd ${jsonParser} + +# compile our parser +javac -cp .:json-org.jar ParseDenseChangelog.java + +#if our compiler failes delete useless denseChangelog_$today.json +if [ $? != 0 ]; then + cd ${rdir} + rm "${rdir}"/denseChangelog_${today}.json + exit 1 +fi + +outfile=${fileout} +if [ -z "fileout" ]; then + outfile="${rdir}"/jsonChangelog_${today}.json +fi + +# make dense changelog +java -cp .:json-org.jar ParseDenseChangelog "${rdir}"/denseChangelog_${today}.json ${outfile} ${endDate} + +# clean up +# move prompt back to original directory +cd ${rdir} +rm ${jsonParser}/ParseDenseChangelog.class +rm "${rdir}"/denseChangelog_${today}.json + +exit 0