├── .travis.yml ├── src └── main │ ├── resources │ └── IssuesDownload.properties │ └── java │ └── io │ └── github │ └── psgs │ └── issuesdownload │ ├── Config.java │ ├── IssuesDownload.java │ └── gui │ ├── GUI.form │ └── GUI.java ├── .gitattributes ├── .gitignore ├── pom.xml └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk6 4 | - openjdk7 5 | notifications: 6 | email: false -------------------------------------------------------------------------------- /src/main/resources/IssuesDownload.properties: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # Issues Download - Download GitHub issues for offline use # 3 | # Made by psgs # 4 | ############################################################ 5 | 6 | GitHub-Token = " " -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /src/main/java/io/github/psgs/issuesdownload/Config.java: -------------------------------------------------------------------------------- 1 | package io.github.psgs.issuesdownload; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.FileNotFoundException; 5 | import java.io.IOException; 6 | import java.util.Properties; 7 | 8 | public class Config { 9 | 10 | public static String githubtoken; 11 | 12 | public static void loadConfiguration() throws FileNotFoundException, IOException { 13 | 14 | Properties config = new Properties(); 15 | config.load(new FileInputStream("IssuesDownload.properties")); 16 | 17 | githubtoken = config.getProperty("GitHub-Token"); 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | 20 | # External tool builders 21 | .externalToolBuilders/ 22 | 23 | # Locally stored "Eclipse launch configurations" 24 | *.launch 25 | 26 | # CDT-specific 27 | .cproject 28 | 29 | # PDT-specific 30 | .buildpath 31 | 32 | ############ 33 | ## Windows 34 | ############ 35 | 36 | # Windows image file caches 37 | Thumbs.db 38 | 39 | # Folder config file 40 | Desktop.ini 41 | 42 | # Installer logs 43 | pip-log.txt 44 | 45 | # Unit test / coverage reports 46 | .coverage 47 | .tox 48 | 49 | #Translations 50 | *.mo 51 | 52 | #Mr Developer 53 | .mr.developer.cfg 54 | 55 | # Mac crap 56 | .DS_Store 57 | 58 | ################# 59 | ## Various 60 | ################# 61 | 62 | # NetBeans 63 | /nbproject 64 | 65 | # Maven 66 | /target 67 | */target 68 | /dependency-reduced-pom.xml 69 | 70 | # vim 71 | .*.sw[a-p] 72 | 73 | # Various other potential build files 74 | /build 75 | /bin 76 | /dist 77 | /manifest.mf 78 | 79 | /world 80 | 81 | # Mac filesystem dust 82 | .DS_Store 83 | 84 | # IntelliJ 85 | *.iml 86 | *.ipr 87 | *.iws 88 | .idea/ 89 | 90 | ############# 91 | ## Gradle 92 | ############# 93 | 94 | .gradle 95 | build 96 | out 97 | 98 | gradlew 99 | gradlew.bat 100 | gradle -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | IssuesDownload 8 | IssuesDownload 9 | 1.0-RELEASE 10 | 11 | 12 | 13 | org.kohsuke 14 | github-api 15 | 1.49 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-shade-plugin 24 | 2.1 25 | 26 | 27 | package 28 | 29 | shade 30 | 31 | 32 | 33 | 35 | io.github.psgs.issuesdownload.IssuesDownload 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Issues Download [![Build Status](https://travis-ci.org/psgs/IssuesDownload.png?branch=master)](https://travis-ci.org/psgs/IssuesDownload) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/psgs/issuesdownload/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 2 | ==================== 3 | 4 | Issues Download is a lightweight java application that will pull issues from a github.com repository, and write them to a .csv file. IssuesDownload uses [Kohsuke Kawaguchi's GitHub API for Java](http://github-api.kohsuke.org) to connect to GitHub when writing issues. 5 | 6 | To run the application once compiled, simply run the .jar file that should have been compiled. 7 | Once the process is complete, a .csv file should contain issue information in the .jar file directory. 8 | Please Note: When the .jar file is run, it will overwrite any other issues.csv files in the jar file directory. 9 | 10 | If downloading Issues doesn't work after multiple tries, you may need to copy the [IssuesDownload properties file](https://github.com/psgs/IssuesDownload/blob/master/src/main/resources/IssuesDownload.properties), and place it in the same directory as the .jar file, then edit the file to contain your [personal or an application access token](https://help.github.com/articles/creating-an-access-token-for-command-line-use). 11 | 12 | Releases 13 | -------- 14 | 15 | - [v1.0](https://github.com/psgs/IssuesDownload/releases/download/v1.0/IssuesDownload-1.0-RELEASE.jar) 16 | 17 | Screenshots 18 | ----------- 19 | 20 | ![Screenshot One](http://i.imgur.com/FBdjxlQ.png) 21 | 22 | CSV Format 23 | ---------- 24 | 25 | The .csv format used is as follows: 26 | Id, Title, Creator, Assignee, Milestone, State, Body Text. 27 | 28 | The first line of the csv file shows the format used, and therefore does not display any information pulled from issues. 29 | 30 | ID | Title | Creator | Assignee | Milestone | State | Body Text 31 | ------------ | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- 32 | 1 | Add a Bitdeli Badge to README | bitdeli-chef | psgs | First-Release | CLOSED | Pull request made by @psgs at https://bitdeli.com 33 | 34 | Compiling 35 | --------- 36 | 37 | Don't feel like compiling? You can download a pre-compiled release from the [Releases section](https://github.com/psgs/IssuesDownload#releases). 38 | 39 | IssuesDownload uses [Apache Maven 3](http://maven.apache.org/) to compile! 40 | 41 | To compile IssuesDownload, simply install [Apache Maven](http://maven.apache.org/), and run: 42 | ```mvn -f pom.xml clean install -P shade``` 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/psgs/issuesdownload/IssuesDownload.java: -------------------------------------------------------------------------------- 1 | package io.github.psgs.issuesdownload; 2 | 3 | import io.github.psgs.issuesdownload.gui.GUI; 4 | import org.kohsuke.github.GHIssue; 5 | import org.kohsuke.github.GHIssueState; 6 | import org.kohsuke.github.GHRepository; 7 | import org.kohsuke.github.GitHub; 8 | 9 | import java.io.FileWriter; 10 | import java.io.IOException; 11 | 12 | public class IssuesDownload { 13 | 14 | public static void main(String[] args) { 15 | try { 16 | Config.loadConfiguration(); 17 | } catch (IOException ex) { 18 | System.out.println("An IOException occurred while loading the configuration!"); 19 | ex.printStackTrace(); 20 | } 21 | GUI.main(args); 22 | } 23 | 24 | public static String saveIssues(String repoDetails, GHIssueState issueState) { 25 | 26 | String[] repoInfo = repoDetails.split("/"); 27 | 28 | try { 29 | GitHub github = GitHub.connectUsingOAuth(Config.githubtoken); 30 | GHRepository repository = github.getUser(repoInfo[0]).getRepository(repoInfo[1]); 31 | 32 | FileWriter writer = new FileWriter("issues.csv"); 33 | writer.append("Id, Title, Creator, Assignee, Milestone, State, Body Text"); 34 | writer.append("\n"); 35 | 36 | for (GHIssue issue : repository.getIssues(issueState)) { 37 | writer.append(String.valueOf(issue.getNumber()) + ","); 38 | writer.append(issue.getTitle() + ","); 39 | writer.append(issue.getUser().getLogin() + ","); 40 | if (issue.getAssignee() != null) { 41 | writer.append(issue.getAssignee().getName() + ","); 42 | } else { 43 | writer.append(" ,"); 44 | } 45 | if (issue.getMilestone() != null) { 46 | writer.append(issue.getMilestone().getTitle() + ","); 47 | } else { 48 | writer.append(" ,"); 49 | } 50 | writer.append(issue.getState() + ","); 51 | writer.append(issue.getBody() + ","); 52 | writer.append("\n"); 53 | } 54 | writer.flush(); 55 | writer.close(); 56 | return "Download Complete!"; 57 | } catch (IOException ex) { 58 | System.out.println("An IOException has occurred!"); 59 | ex.printStackTrace(); 60 | if (ex.getMessage().equalsIgnoreCase("api.github.com")) { 61 | return "An error has occurred reaching " + ex.getMessage() + "! Please check your network connection."; 62 | } 63 | } 64 | return "An error has occurred!"; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/io/github/psgs/issuesdownload/gui/GUI.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /src/main/java/io/github/psgs/issuesdownload/gui/GUI.java: -------------------------------------------------------------------------------- 1 | package io.github.psgs.issuesdownload.gui; 2 | 3 | import io.github.psgs.issuesdownload.IssuesDownload; 4 | import org.kohsuke.github.GHIssueState; 5 | 6 | import java.awt.*; 7 | 8 | public class GUI extends javax.swing.JFrame { 9 | 10 | public GUI() { 11 | initComponents(); 12 | } 13 | 14 | @SuppressWarnings("unchecked") 15 | // //GEN-BEGIN:initComponents 16 | private void initComponents() { 17 | 18 | jPanel1 = new javax.swing.JPanel(); 19 | jLabel1 = new javax.swing.JLabel(); 20 | jTextField1 = new javax.swing.JTextField(); 21 | jTextField2 = new javax.swing.JTextField(); 22 | jButton1 = new javax.swing.JButton(); 23 | jRadioButton1 = new javax.swing.JRadioButton(); 24 | jRadioButton2 = new javax.swing.JRadioButton(); 25 | 26 | setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); 27 | 28 | jLabel1.setFont(new java.awt.Font("Tahoma", Font.BOLD, 15)); // NOI18N 29 | jLabel1.setText(" Issues Download v1.0-SNAPSHOT"); 30 | 31 | jTextField1.setText("Path to Repository (psgs/IssuesDownload)"); 32 | 33 | jButton1.setText("Download"); 34 | jButton1.addActionListener(new java.awt.event.ActionListener() { 35 | public void actionPerformed(java.awt.event.ActionEvent evt) { 36 | jButton1ActionPerformed(evt); 37 | } 38 | }); 39 | 40 | jRadioButton1.setSelected(true); 41 | jRadioButton1.setText("Open"); 42 | jRadioButton1.addActionListener(new java.awt.event.ActionListener() { 43 | public void actionPerformed(java.awt.event.ActionEvent evt) { 44 | jRadioButton1ActionPerformed(evt); 45 | } 46 | }); 47 | 48 | jRadioButton2.setText("Closed"); 49 | jRadioButton2.addActionListener(new java.awt.event.ActionListener() { 50 | public void actionPerformed(java.awt.event.ActionEvent evt) { 51 | jRadioButton2ActionPerformed(evt); 52 | } 53 | }); 54 | 55 | javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); 56 | jPanel1.setLayout(jPanel1Layout); 57 | jPanel1Layout.setHorizontalGroup( 58 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 59 | .addGroup(jPanel1Layout.createSequentialGroup() 60 | .addContainerGap() 61 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 62 | .addComponent(jTextField2) 63 | .addComponent(jLabel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE) 64 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() 65 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) 66 | .addGroup(jPanel1Layout.createSequentialGroup() 67 | .addGap(0, 0, Short.MAX_VALUE) 68 | .addComponent(jRadioButton1) 69 | .addGap(53, 53, 53) 70 | .addComponent(jRadioButton2) 71 | .addGap(36, 36, 36)) 72 | .addComponent(jTextField1)) 73 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 74 | .addComponent(jButton1))) 75 | .addContainerGap()) 76 | ); 77 | jPanel1Layout.setVerticalGroup( 78 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 79 | .addGroup(jPanel1Layout.createSequentialGroup() 80 | .addContainerGap() 81 | .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) 82 | .addGap(18, 18, 18) 83 | .addComponent(jTextField2, javax.swing.GroupLayout.DEFAULT_SIZE, 160, Short.MAX_VALUE) 84 | .addGap(9, 9, 9) 85 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 86 | .addComponent(jRadioButton1) 87 | .addComponent(jRadioButton2)) 88 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 89 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 90 | .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE) 91 | .addComponent(jButton1)) 92 | .addContainerGap()) 93 | ); 94 | 95 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); 96 | getContentPane().setLayout(layout); 97 | layout.setHorizontalGroup( 98 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 99 | .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 100 | ); 101 | layout.setVerticalGroup( 102 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 103 | .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 104 | ); 105 | 106 | pack(); 107 | }// //GEN-END:initComponents 108 | 109 | private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed 110 | if (jRadioButton1.isSelected()) { 111 | jTextField2.setText(IssuesDownload.saveIssues(jTextField1.getText(), GHIssueState.OPEN)); 112 | } else { 113 | jTextField2.setText(IssuesDownload.saveIssues(jTextField1.getText(), GHIssueState.CLOSED)); 114 | } 115 | }//GEN-LAST:event_jButton1ActionPerformed 116 | 117 | private void jRadioButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed 118 | jRadioButton2.setSelected(false); 119 | }//GEN-LAST:event_jButton1ActionPerformed 120 | 121 | private void jRadioButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed 122 | jRadioButton1.setSelected(false); 123 | }//GEN-LAST:event_jButton1ActionPerformed 124 | 125 | public static void main(String args[]) { 126 | /* Set the Nimbus look and feel */ 127 | // 128 | /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. 129 | * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 130 | */ 131 | try { 132 | for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { 133 | if ("Nimbus".equals(info.getName())) { 134 | javax.swing.UIManager.setLookAndFeel(info.getClassName()); 135 | break; 136 | } 137 | } 138 | } catch (ClassNotFoundException ex) { 139 | java.util.logging.Logger.getLogger(GUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 140 | } catch (InstantiationException ex) { 141 | java.util.logging.Logger.getLogger(GUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 142 | } catch (IllegalAccessException ex) { 143 | java.util.logging.Logger.getLogger(GUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 144 | } catch (javax.swing.UnsupportedLookAndFeelException ex) { 145 | java.util.logging.Logger.getLogger(GUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 146 | } 147 | // 148 | 149 | /* Create and display the form */ 150 | java.awt.EventQueue.invokeLater(new Runnable() { 151 | public void run() { 152 | new GUI().setVisible(true); 153 | } 154 | }); 155 | } 156 | 157 | // Variables declaration - do not modify//GEN-BEGIN:variables 158 | private javax.swing.JButton jButton1; 159 | private javax.swing.JLabel jLabel1; 160 | private javax.swing.JPanel jPanel1; 161 | private javax.swing.JRadioButton jRadioButton1; 162 | private javax.swing.JRadioButton jRadioButton2; 163 | private javax.swing.JTextField jTextField1; 164 | private javax.swing.JTextField jTextField2; 165 | // End of variables declaration//GEN-END:variables 166 | } 167 | --------------------------------------------------------------------------------