From dd7961bc443c732d0bbdd6bda47d6c2cfda5b290 Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 17 May 2011 22:06:07 -0400 Subject: [PATCH] Building site content and documentation from Markdown. --- .gitignore | 1 + build.xml | 51 +++++-- distrib/gitblit.properties | 6 +- ...{installService.bat => installService.cmd} | 0 ...tallService64.bat => installService64.cmd} | 0 distrib/makekeystore.cmd | 2 + ...nstallService.bat => uninstallService.cmd} | 0 ...llService64.bat => uninstallService64.cmd} | 0 docs/00_overview.mkd | 87 +++++++++++ docs/01_configuration.mkd | 73 ++++++++++ docs/01_eclipse.mkd | 5 + docs/01_faq.mkd | 1 + docs/01_releases.mkd | 1 + docs/01_screenshots.mkd | 3 + docs/architecture.odg | Bin 0 -> 13340 bytes docs/architecture.png | Bin 0 -> 16892 bytes docs/page_footer.html | 6 + docs/page_header.html | 17 +++ src/com/gitblit/BuildSite.java | 136 ++++++++++++++++++ src/com/gitblit/Constants.java | 2 + src/com/gitblit/wicket/resources/markdown.css | 13 ++ 21 files changed, 393 insertions(+), 11 deletions(-) rename distrib/{installService.bat => installService.cmd} (100%) rename distrib/{installService64.bat => installService64.cmd} (100%) create mode 100644 distrib/makekeystore.cmd rename distrib/{UninstallService.bat => uninstallService.cmd} (100%) rename distrib/{UninstallService64.bat => uninstallService64.cmd} (100%) create mode 100644 docs/00_overview.mkd create mode 100644 docs/01_configuration.mkd create mode 100644 docs/01_eclipse.mkd create mode 100644 docs/01_faq.mkd create mode 100644 docs/01_releases.mkd create mode 100644 docs/01_screenshots.mkd create mode 100644 docs/architecture.odg create mode 100644 docs/architecture.png create mode 100644 docs/page_footer.html create mode 100644 docs/page_header.html create mode 100644 src/com/gitblit/BuildSite.java diff --git a/.gitignore b/.gitignore index 2aa1fab6..6ba70733 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /*.zip /gitblit.properties /users.properties +/site \ No newline at end of file diff --git a/build.xml b/build.xml index 22f23360..a25ba329 100644 --- a/build.xml +++ b/build.xml @@ -3,27 +3,27 @@ - + - + - - - - - - + + + + + + Building Git:Blit ${gb.version} - + @@ -31,7 +31,7 @@ - + @@ -98,5 +98,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/distrib/gitblit.properties b/distrib/gitblit.properties index bcbd3db4..e13b60cf 100644 --- a/distrib/gitblit.properties +++ b/distrib/gitblit.properties @@ -146,11 +146,15 @@ server.httpPort = 0 server.httpsPort = 443 # Specify the interface for Jetty to bind the standard connector. -# You may specify an ip or an empty value to bind to all interfaces. +# You may specify an ip or an empty value to bind to all interfaces. +# Specifying localhost will result in Git:Blit ONLY listening to requests to +# localhost. server.httpBindInterface = localhost # Specify the interface for Jetty to bind the secure connector. # You may specify an ip or an empty value to bind to all interfaces. +# Specifying localhost will result in Git:Blit ONLY listening to requests to +# localhost. server.httpsBindInterface = localhost # Password for SSL keystore. diff --git a/distrib/installService.bat b/distrib/installService.cmd similarity index 100% rename from distrib/installService.bat rename to distrib/installService.cmd diff --git a/distrib/installService64.bat b/distrib/installService64.cmd similarity index 100% rename from distrib/installService64.bat rename to distrib/installService64.cmd diff --git a/distrib/makekeystore.cmd b/distrib/makekeystore.cmd new file mode 100644 index 00000000..34a11b21 --- /dev/null +++ b/distrib/makekeystore.cmd @@ -0,0 +1,2 @@ +@del keystore +@keytool -keystore keystore -alias localhost -genkey -keyalg RSA -dname "CN=localhost, OU=Git:Blit, O=Git:Blit, L=Some Town, ST=Some State, C=US" \ No newline at end of file diff --git a/distrib/UninstallService.bat b/distrib/uninstallService.cmd similarity index 100% rename from distrib/UninstallService.bat rename to distrib/uninstallService.cmd diff --git a/distrib/UninstallService64.bat b/distrib/uninstallService64.cmd similarity index 100% rename from distrib/UninstallService64.bat rename to distrib/uninstallService64.cmd diff --git a/docs/00_overview.mkd b/docs/00_overview.mkd new file mode 100644 index 00000000..63898f6c --- /dev/null +++ b/docs/00_overview.mkd @@ -0,0 +1,87 @@ +## Overview +Git:Blit is an open-source, integrated pure-Java stack for managing, viewing, and serving [Git](http://git-scm.com) repositories. +Its designed primarily as a tool for small workgroups who want to host Git repositories on a Windows machine. + +Of course, since its pure-Java it should run with any JVM on any platform, but there are already [many compelling Git solutions](https://git.wiki.kernel.org/index.php/InterfacesFrontendsAndTools) for non-Windows environments. + +### Current Version + +[{0}](http://gitblit.com/{1}) based on [{2}](http://eclipse.org/jgit)   (*{3}*) + +### Features +- Out-of-the-box integrated stack requiring minimal configuration +- JGit SmartHTTP Servlet +- Web and Git Servlet authentication +- Four repository access control configurations + - *Anonymous View, Clone & Push* + - *Authenticated Push* + - *Authenticated Clone & Push* + - *Authenticated View, Clone & Push* +- Gitweb inspired UI (mostly plain html) +- Repository administration through web UI +- User administration through web UI +- Automatically generates a self-signed certificate for https communications +- Dates can optionally be displayed using browser's reported timezone +- Author and Committer email address display can be controlled +- Syntax highlighting +- Customizable regular expression handling for commit messages +- Simple repository stats +- Simple text file for server configuration +- Simple text file for users configuration +- Optional integrated Ticgit +- Optional integrated Markdown + +### Limitations +- HTTP/HTTPS are the only supported protocols +- Access controls are not path-based, they are repository-based +- Only admin users can create repositories +- Git:Blit is a full-stack solution, its not just a webapp so at this time there is no WAR build + +### Todo List +- Review spots where Git:Blit can cache data instead of abusing the disk +- Ticgit activity/timeline +- Ticgit query feature with paging support +- Ticgit ticket change history +- Implement Markdown editing +- View images on Blob page +- View other binary files Blob page + +### License +TBD + +### Architecture + +![block diagram](architecture.png "Git Blit Architecture") + +### Bundled Dependencies +The following dependencies are bundled with the Git:Blit zip file. + +- [google-code-prettify](http://code.google.com/p/google-code-prettify) +- [JavaService](http://forge.ow2.org/projects/javaservice) +- icons courtesy of [FatCow Hosting](http://www.fatcow.com/free-icons) + +### Downloaded Dependencies +The following dependencies are automatically downloaded from the Apache Maven repository and from the Eclipse Maven repository when Git:Blit is launched for the first time. + +- [JGit](http://eclipse.org/jgit) +- [Wicket](http://wicket.apache.org) +- [WicketStuff GoogleCharts](https://github.com/wicketstuff/core/wiki/GoogleCharts) +- [MarkdownPapers](http://markdown.tautua.org) +- [Jetty](http://eclipse.org/jetty) +- [SLF4J](http://www.slf4j.org) +- [Log4j](http://logging.apache.org/log4j) +- [JCommander](http://jcommander.org) +- [BouncyCastle](http://www.bouncycastle.org) + +### Building +Eclipse is recommended for development as the project settings are preconfigured. + +1. Clone the git repository from here. +2. Import the gitblit project into your Eclipse workspace.
+*There will be lots of build errors.* +3. Using Ant, execute the `build.xml` script in the project root.
+*This will download all necessary build dependencies and will also generate the Keys class for accessing settings.* +4. Select your gitblit project root and **Refresh** the project, this should correct all build problems. +5. Review the settings in `gitblit.properties` in your project root.
+Make sure you set an appropriate value for *git.repositoriesFolder*. +6. Execute the *com.gitblit.Launcher* class to start Git:Blit. \ No newline at end of file diff --git a/docs/01_configuration.mkd b/docs/01_configuration.mkd new file mode 100644 index 00000000..9e8a9f06 --- /dev/null +++ b/docs/01_configuration.mkd @@ -0,0 +1,73 @@ +## Setup and Configuration + +1. Download and unzip Git:Blit.
+*Its best to eliminate spaces in the path name as that can cause troubleshooting headaches.* +2. The server itself is configured through a simple text file.
+Open `gitblit.properties` in your favorite text editor and make sure to review and set: + - *git.repositoryFolder* + - *server.tempFolder* + - *server.httpBindInterface* and *server.httpsBindInterface* + - *server.storePassword*
+**NOTE:**
+Its recommended to use **https** wherever possible instead of http because passwords are transmitted as clear text! +3. Execute `gitblit.cmd` or `java -jar gitblit.jar` from a command-line +4. Wait a minute or two while all dependencies are downloaded and your self-signed certificate is generated. +5. Open your browser to or depending on your chosen configuration. +6. Click the *Login* link and enter the default administrator credentials: **admin / admin**
+**NOTE:**
+Make sure to change the administrator username and/or password!! + +### Administering Repositories +Repositories can be created, edited, and deleted through the web UI. They may also be created, edited, and deleted from the command-line using real Git or your favorite file manager and text editor. + +All repository settings are stored within the repository `.git/config` file under the *gitblit* section. + + [gitblit] + description = master repository + owner = Joe Owner + useTickets = false + useDocs = true + showRemoteBranches = false + accessRestriction = clone + +#### Repository Names +Repository names must be unique and are case-insensitive. The name must be composed of letters, digits, or `/ _ - .`
+Whitespace is illegal. + +### Administering Users +In contrast, all users are stored in the `users.properties` file or in the file your specified in `gitblit.properties`.
+The format of `users.properties` follows Jetty's convention for HashRealms: + + username,password,role1,role2,role3... + +#### Usernames +Usernames must be unique and are case-insensitive.
+Whitespace is illegal. + +#### Passwords +User passwords are CASE-SENSITIVE and may be *plain*, *md5*, or *crypt* formatted (see `gitblit.properties` -> *realm.passwordStorage*). + +#### User Roles +There is only one actual *role* in Git:Blit and that is *#admin* which grants administrative powers to that user. Administrators automatically have access to all repositories. All other *roles* are actually repository names. If a repository is access-restricted, the user must have the repository's name within his/her roles to bypass the access restriction. This is how users are granted access to a restricted repository. + +### Creating your own Self-Signed Certificate + +Review the contents of the `makekeystore.cmd` or `makekeystore_jdk.cmd`script and execute it. Voila. + +### Running as a Service +Review the contents of the `installService.cmd` or `installService64.cmd`, as appropriate for your JVM.
+Set the *JDK* variable in the script to the location of your JDK, add any necessary start parameters, and execute the script. + +#### Command-Line Parameters + --tempFolder Server temp folder + --repositoriesFolder Git Repositories Folder + --realmFile Users Realm Hash File + --useNio Use NIO Connector else use Socket Connector. + --httpPort HTTP port for to serve. (port <= 0 will disable this connector) + --httpsPort HTTPS port to serve. (port <= 0 will disable this connector) + --storePassword Password for SSL (https) keystore. + --shutdownPort Port for Shutdown Monitor to listen on. (port <= 0 will disable this monitor) + +**Example** + + java -jar gitblit.jar --realmFile c:\myrealm.txt --storePassword something \ No newline at end of file diff --git a/docs/01_eclipse.mkd b/docs/01_eclipse.mkd new file mode 100644 index 00000000..93fd5315 --- /dev/null +++ b/docs/01_eclipse.mkd @@ -0,0 +1,5 @@ +## Eclipse Tips + +verifySsl + +how to push new unshared project to new repository \ No newline at end of file diff --git a/docs/01_faq.mkd b/docs/01_faq.mkd new file mode 100644 index 00000000..c92bfabd --- /dev/null +++ b/docs/01_faq.mkd @@ -0,0 +1 @@ +## Frequently Asked Questions diff --git a/docs/01_releases.mkd b/docs/01_releases.mkd new file mode 100644 index 00000000..7c0c2040 --- /dev/null +++ b/docs/01_releases.mkd @@ -0,0 +1 @@ +## Release History diff --git a/docs/01_screenshots.mkd b/docs/01_screenshots.mkd new file mode 100644 index 00000000..666e943f --- /dev/null +++ b/docs/01_screenshots.mkd @@ -0,0 +1,3 @@ +## Screenshots + +![test](gitblt_25.png "Optional Title") \ No newline at end of file diff --git a/docs/architecture.odg b/docs/architecture.odg new file mode 100644 index 0000000000000000000000000000000000000000..fc4ea7b726948157883d30ffe70843b1aeaf1b19 GIT binary patch literal 13340 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

~p-i}aES+S+x< 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 TZ1uqBFn| +

+ + \ No newline at end of file diff --git a/docs/page_header.html b/docs/page_header.html new file mode 100644 index 00000000..d282f54d --- /dev/null +++ b/docs/page_header.html @@ -0,0 +1,17 @@ + + + Git:Blit + + + + + + + + +
\ No newline at end of file diff --git a/src/com/gitblit/BuildSite.java b/src/com/gitblit/BuildSite.java new file mode 100644 index 00000000..ae85c062 --- /dev/null +++ b/src/com/gitblit/BuildSite.java @@ -0,0 +1,136 @@ +package com.gitblit; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.Parameters; +import com.gitblit.utils.MarkdownUtils; + +public class BuildSite { + + public static void main(String... args) { + Params params = new Params(); + JCommander jc = new JCommander(params); + try { + jc.parse(args); + } catch (ParameterException t) { + usage(jc, t); + } + + File sourceFolder = new File(params.sourceFolder); + File destinationFolder = new File(params.outputFolder); + File[] markdownFiles = sourceFolder.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith(".mkd"); + } + }); + Arrays.sort(markdownFiles); + + System.out.println(MessageFormat.format("Generating site from {0} Markdown Docs in {1} ", markdownFiles.length, sourceFolder.getAbsolutePath())); + String linkPattern = "{1}"; + StringBuilder sb = new StringBuilder(); + for (File file : markdownFiles) { + String displayName = getDocumentName(file); + String fileName = displayName + ".html"; + sb.append(MessageFormat.format(linkPattern, fileName, displayName)); + sb.append(" | "); + } + sb.setLength(sb.length() - 3); + sb.trimToSize(); + String html_header = readContent(new File(params.pageHeader)); + String html_footer = readContent(new File(params.pageFooter)); + final String links = sb.toString(); + final String header = MessageFormat.format(html_header, Constants.FULL_NAME, links); + final String date = new SimpleDateFormat("yyyy MMM dd").format(new Date()); + final String footer = MessageFormat.format(html_footer, "generated " + date); + for (File file : markdownFiles) { + try { + String displayName = getDocumentName(file); + String fileName = displayName + ".html"; + System.out.println(MessageFormat.format(" {0} => {1}", file.getName(), fileName)); + InputStreamReader reader = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")); + String content = MarkdownUtils.transformMarkdown(reader); + if (displayName.equalsIgnoreCase("overview")) { + content = MessageFormat.format(content, Constants.VERSION, "gitblit-" + Constants.VERSION + ".zip", Constants.getJGitVersion(), date); + } + OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(destinationFolder, fileName)), Charset.forName("UTF-8")); + writer.write(header); + writer.write(content); + writer.write(footer); + reader.close(); + writer.close(); + } catch (Throwable t) { + System.err.println("Failed to transform " + file.getName()); + t.printStackTrace(); + } + } + } + + private static String readContent(File file) { + StringBuilder sb = new StringBuilder(); + try { + InputStreamReader is = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")); + BufferedReader reader = new BufferedReader(is); + String line = null; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + reader.close(); + } catch (Throwable t) { + System.err.println("Failed to read content of " + file.getAbsolutePath()); + t.printStackTrace(); + } + return sb.toString(); + } + + private static String getDocumentName(File file) { + String displayName = file.getName().substring(0, file.getName().lastIndexOf('.')).toLowerCase(); + // trim leading ##_ which is to control display order + return displayName.substring(3); + } + + private static void usage(JCommander jc, ParameterException t) { + System.out.println(Constants.getRunningVersion()); + System.out.println(); + if (t != null) { + System.out.println(t.getMessage()); + System.out.println(); + } + if (jc != null) { + jc.usage(); + } + System.exit(0); + } + + @Parameters(separators = " ") + private static class Params { + + @Parameter(names = { "--sourceFolder" }, description = "Markdown Source Folder", required = true) + public String sourceFolder; + + @Parameter(names = { "--outputFolder" }, description = "HTML Ouptut Folder", required = true) + public String outputFolder; + + @Parameter(names = { "--pageHeader" }, description = "Page Header HTML Snippet", required = true) + public String pageHeader; + + @Parameter(names = { "--pageFooter" }, description = "Page Footer HTML Snippet", required = true) + public String pageFooter; + + } +} diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index 1b4a5188..9cf6a54f 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -3,6 +3,8 @@ package com.gitblit; public class Constants { public final static String NAME = "Git:Blit"; + + public final static String FULL_NAME = "Git:Blit - a Pure Java Git Server"; // The build script extracts this exact line so be careful editing it // and only use A-Z a-z 0-9 .-_ in the string. diff --git a/src/com/gitblit/wicket/resources/markdown.css b/src/com/gitblit/wicket/resources/markdown.css index ce48cefb..41bc4392 100644 --- a/src/com/gitblit/wicket/resources/markdown.css +++ b/src/com/gitblit/wicket/resources/markdown.css @@ -3,6 +3,7 @@ */ div.markdown { + line-height: 1.4em; } div.markdown h1, @@ -51,4 +52,16 @@ div.markdown code { div.markdown a { text-decoration: underline; +} + +div.markdown ul, div.markdown ol { + padding-left: 30px; +} + +div.markdown li { + margin: 0.2em 0 0 0em; padding: 0px; +} + +div.markdown em { + color: #b05000; } \ No newline at end of file -- 2.39.5