Skip to content

Commit 816adf8

Browse files
committed
v1.0 With unit tests.
1 parent 28387a6 commit 816adf8

File tree

5 files changed

+337
-3
lines changed

5 files changed

+337
-3
lines changed

.gitignore

Whitespace-only changes.

README.md

+68-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,69 @@
1-
stata-git
2-
=========
1+
## Description
2+
Manages [git](http://git-scm.org) repositories in Stata. It installs, updates, lists and removes git repositories containing Stata user-written programs. An alternative to sharing through `ssc` or `net`
3+
4+
## Installation
5+
6+
### Manually (safest until published on ssc)
7+
1. Clone this repo
8+
2. Copy the `.ado` and `.sthlp` file to your [personal ado directory](http://www.stata.com/support/faqs/programming/personal-ado-directory/) under the `g` directory (create it if it does not exist).
9+
10+
### Using ssc (TBC)
11+
In Stata, type:
12+
13+
`ssc install git`
14+
15+
After this you can keep `git` itself updated using `git` by typing `git update git`
16+
17+
18+
## Usage
19+
20+
### install repository
21+
Install a program from _repository_ (url or filepath)
22+
23+
```bash
24+
git install https://github.com/coderigo/stata-switch
25+
```
26+
27+
### uninstall program
28+
Uninstall program previously installed with `git`
29+
30+
```bash
31+
git uninstall switch
32+
```
33+
34+
### update program
35+
Update program previously installed with `git`
36+
37+
```bash
38+
git update switch
39+
```
40+
41+
### list repositories
42+
List all repositories managed by `git`
43+
44+
```bash
45+
git list
46+
```
47+
48+
## Creating a git repository containing a Stata module}
49+
50+
A few handy tips to make it work best `git`:
51+
52+
1. Use the snake-case format for your repository name and prefix it with "stata". For example, if your program {it: helloWorld}, your repository should be called {it:stata-hello-world}.{p_end}
53+
54+
2. Use the {it: camelCase} for your program name and any installable other auxiliary files including the {it: .sthlp} file.{p_end}
55+
56+
3. Place all the files you want to be copied over in the root of your repository.{p_end}
57+
58+
4. It works nicely (and it's easier for community collaboration) if you host your repository on a public repo like {it:github}{p_end}.
59+
60+
## Contributing
61+
Happy to take requests.
62+
Please add to the `tests.do` some sort of test, or run to make sure nothing's broken.
63+
64+
## Limitations
65+
66+
1. Have only tested this out on OSX, but feel it _should_ work the same with *nix OSs. In theory should work on Windows, but have not tested it.
67+
68+
2. It requires repositories and program names to follow the `snake-case` and `camelCase` format respectively (there is no obvious way to read file data into a variable with Stata. Happy to be corrected)
369

4-
Installs and manages Stata programs tracked as git repositories.

git.ado

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
*! version 1.0
2+
version 12.0
3+
capture program drop git
4+
program define git
5+
syntax anything(name=gitArgs id="git command and arguments")
6+
7+
/**
8+
* This requires that you have git installed and accessible through the command line.
9+
* For instructions on installing git, see: http://git-scm.com/downloads
10+
*/
11+
di as red "WARNING:"
12+
di as red "This program requires git to be accessible through the command line."
13+
di as red "See http://git-scm.com/downloads"
14+
/*
15+
OS-dependent vars
16+
*/
17+
local os = "`c(os)'"
18+
local adoPlusDir = "`c(sysdir_plus)'"
19+
if("`os'" != "Windows"){
20+
local adoPlusDir = subinstr("`adoPlusDir'","~","/Users/`c(username)'",.)
21+
}
22+
local adoDir = trim(subinstr("`adoPlusDir'","ado/plus/","",.))
23+
local gitDir = "`adoDir'git/"
24+
local copyCmd = "cp" /* Defaults to *nix command */
25+
local deleteCmd = "rm" /* Defaults to *nix command */
26+
local origDir = "`c(pwd)'"
27+
local lsCmd = "ls" /* Defaults to *nix command */
28+
local rmdirCmd = "rm -rf" /* Defaults to *nix command */
29+
if("`os'" == "Windows"){
30+
local copyCmd = "copy"
31+
local deleteCmd = "erase"
32+
local lsCmd = "dir"
33+
local rmdirCmd = "rmdir /Q /S"
34+
}
35+
36+
/*
37+
Make the git program dir if not there and cd to it
38+
*/
39+
capture confirm file "`gitDir'"
40+
if _rc!=0{
41+
mkdir "`gitDir'"
42+
}
43+
qui cd "`gitDir'"
44+
45+
/*
46+
Command parsing
47+
*/
48+
if(strpos("`gitArgs'"," ") >0){
49+
local gitCommand = trim(substr("`gitArgs'",1, strpos("`gitArgs'"," ")))
50+
}
51+
else{
52+
local gitCommand = trim("`gitArgs'")
53+
}
54+
local gitParam1 = trim(subinstr("`gitArgs'","`gitCommand'","",1))
55+
56+
/* Translate "install" to "clone". "install" is used to be consistent with the ssc command */
57+
if("`gitCommand'" == "install"){
58+
59+
/* Get the name of the repo - Stata has no equivalent to JS (or others) of string split */
60+
local repoName = "`gitParam1'"
61+
local separatorPos = strpos("`repoName'","/")
62+
while `separatorPos' > 0 {
63+
local repoName = trim(substr("`repoName'", `separatorPos'+1, length("`repoName'")-`separatorPos'))
64+
local separatorPos = strpos("`repoName'","/")
65+
}
66+
67+
/* Get the name of the command snake-cased */
68+
local commandSnakeName = "`repoName'"
69+
local dashPos = strpos("`commandSnakeName'", "-")
70+
while `dashPos' > 0 {
71+
local commandSnakeName = trim(substr("`commandSnakeName'", `dashPos'+1, length("`commandSnakeName'")-`dashPos'))
72+
local dashPos = strpos("`commandSnakeName'", "-")
73+
}
74+
75+
/* Create the repo */
76+
local repoDir = "`gitDir'`repoName'/"
77+
capture confirm file "`repoDir'"
78+
if _rc!=0{
79+
mkdir "`repoDir'"
80+
}
81+
else{
82+
/* Return to old directory */
83+
di as green "`repoName' already installed"
84+
di as green "type - git update `repoName' - to get the latest and greatest."
85+
qui cd "`origDir'"
86+
exit
87+
}
88+
shell git clone "`gitParam1'"
89+
90+
/* Copy all .ado and .sthlp files to the right directory */
91+
local programFirstLetter = lower(substr(subinstr("`repoName'","stata-","",.),1,1))
92+
local programDir = "../ado/plus/`programFirstLetter'/"
93+
capture confirm file "`programDir'"
94+
if _rc!=0{
95+
mkdir "`programDir'"
96+
}
97+
qui cd "`repoName'"
98+
shell `copyCmd' *.ado "`adoPlusDir'`programFirstLetter'"
99+
capture shell `copyCmd' *.sthlp "`adoPlusDir'`programFirstLetter'"
100+
capture shell `copyCmd' *.mmat "`adoPlusDir'`programFirstLetter'"
101+
102+
/* Return to old directory */
103+
qui cd "`origDir'"
104+
105+
di as green "Done installing `commandSnakeName' from the `repoName' git repository"
106+
di as green "Type -help `commandSnakeName'-"
107+
108+
}
109+
110+
/* Translate "uninstall" to just removing the repo */
111+
if("`gitCommand'" == "uninstall" | "`gitCommand'" == "update"){
112+
113+
/* Convert from camel case to snake case*/
114+
local commandCamelCase = "`gitParam1'"
115+
local repoName = "stata-`commandCamelCase'"
116+
local lengthOfName = length("`repoName'")
117+
local programFirstLetter = substr("`commandCamelCase'",1,1)
118+
if(length("`repoName'") > 1){
119+
forvalues pos = 2/`lengthOfName'{
120+
local charAtPos = substr("`repoName'",`pos',1)
121+
if(lower("`charAtPos'") != "`charAtPos'"){
122+
local repoName = substr("`repoName'",1,`pos'-1) + "-" + lower("`charAtPos'") + substr("`repoName'",`pos'+1,length("`repoName'")-`pos')
123+
}
124+
}
125+
}
126+
127+
/* Translate "update" to -git pull origin- */
128+
if("`gitCommand'" == "update"){
129+
di as green "Checking for updates for `commandCamelCase' (git repository name: `repoName')..."
130+
qui cd "`repoName'"
131+
shell git pull origin
132+
shell `copyCmd' `commandCamelCase'.ado "`adoPlusDir'`programFirstLetter'"
133+
capture shell `copyCmd' `commandCamelCase'.sthlp "`adoPlusDir'`programFirstLetter'"
134+
capture shell `copyCmd' `commandCamelCase'.mmat "`adoPlusDir'`programFirstLetter'"
135+
qui cd "`origDir'"
136+
}
137+
138+
/* Uninstall nukes all */
139+
if("`gitCommand'" == "uninstall"){
140+
capture confirm file "`repoName'"
141+
if _rc!=0{
142+
di as red "-`commandCamelCase'- not installed via -git- command"
143+
}
144+
else{
145+
qui shell `rmdirCmd' `repoName'
146+
qui cd "`adoPlusDir'`programFirstLetter'"
147+
qui erase "`commandCamelCase'.ado"
148+
qui capture erase "`commandCamelCase'.sthlp"
149+
qui capture erase "`commandCamelCase'.mmat"
150+
qui cd "`origDir'"
151+
di as green "Git repository `repoName' and command -`commandCamelCase'- removed."
152+
}
153+
}
154+
155+
}
156+
157+
/* "list" lists all git-managed repos */
158+
if("`gitCommand'" == "list"){
159+
di as yellow "Git repositories installed using -git- command in"
160+
di as yellow "`gitDir':"
161+
di as green "note: when used in Stata, the stata- prefix is stripped and the "
162+
di as green " program name is snake-cased"
163+
di as green " e.g. repository stata-my-program becomes -myProgram- when used in Stata"
164+
shell `lsCmd'
165+
qui cd "`origDir'"
166+
}
167+
168+
end

git.sthlp

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{smcl}
2+
{cmd:help git} {right:also see: {help ssc }}
3+
{hline}
4+
5+
{title:Title}
6+
7+
{p2colset 5 17 19 2}{...}
8+
{p2col :{hi: git} {hline 2}}Manage git repositories through a series of basic commands. It installs, updates, lists and removes git repositories containing Stata user-written programs. An alternative to sharing through {cmd: ssc} or {cmd: net}{p_end}
9+
{p2colreset}{...}
10+
11+
12+
{title:Syntax}
13+
14+
{p 8 13 5}
15+
{cmd:git} {it: command}
16+
17+
{synoptset 21 tabbed}{...}
18+
{synopthdr: command}
19+
{synoptline}
20+
{synopt : {cmd:install} {it:repository}}Install program in {it:repository} (url or filepath).{p_end}
21+
{synopt : {cmd:uninstall} {it:programName}}Uninstall {it: programName} and its repository previously installed with {cmd: git install}.{p_end}
22+
{synopt : {cmd:update} {it:programName}}Update {it: programName}'s repository previously installed with {cmd: git install}.{p_end}
23+
{synopt : {cmd:list}}List all repositories managed by {cmd: git}.{p_end}
24+
25+
{title:Description}
26+
27+
{pstd}
28+
{opt git} Manages git repositories through a series of basic commands. It installs, updates, lists and removes git repositories containing Stata user-written programs. An alternative to sharing through {cmd: ssc} or {cmd: net}. Git repositories are saved in the same location as the {cmd: ado} and {cmd: personal} directories{p_end}
29+
30+
{title:Creating a git repository containing a Stata module}
31+
32+
{p 4 4 2} A few handy tips to make it work with {cmd: git}:{p_end}
33+
34+
{p 4 4 2} 1. Use the snake-case format for your repository name and prefix it with "stata". For example, if your program {it: helloWorld}, your repository should be called {it:stata-hello-world}.{p_end}
35+
36+
{p 4 4 3} 2. Use the {it: camelCase} for your program name and any installable other auxiliary files including the {it: .sthlp} file.{p_end}
37+
38+
{p 4 4 3} 3. Place all the files you want to be copied over in the root of your repository.{p_end}
39+
40+
{p 4 4 3} 4. It works nicely (and it's easier for community collaboration) if you host your repository on a public repo like {it:github}{p_end}.
41+
42+
{title:Examples}
43+
44+
{phang}{cmd:. git install https://github.com/coderigo/stata-switch}{p_end}
45+
{phang}{cmd:. git update switch}{p_end}
46+
{phang}{cmd:. git list}{p_end}
47+
{phang}{cmd:. git uninstall switch}{p_end}

tests.do

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
do "git.ado"
2+
3+
local os = "`c(os)'"
4+
local adoPlusDir = "`c(sysdir_plus)'"
5+
if("`os'" != "Windows"){
6+
local adoPlusDir = subinstr("`adoPlusDir'","~","/Users/`c(username)'",.)
7+
}
8+
local adoDir = trim(subinstr("`adoPlusDir'","ado/plus/","",.))
9+
local gitDir = "`adoDir'git/"
10+
local copyCmd = "cp" /* Defaults to *nix command */
11+
local deleteCmd = "rm" /* Defaults to *nix command */
12+
local origDir = "`c(pwd)'"
13+
local lsCmd = "ls" /* Defaults to *nix command */
14+
local rmdirCmd = "rm -rf" /* Defaults to *nix command */
15+
if("`os'" == "Windows"){
16+
local copyCmd = "copy"
17+
local deleteCmd = "erase"
18+
local lsCmd = "dir"
19+
local rmdirCmd = "rmdir /Q /S"
20+
}
21+
22+
/*Test installing*/
23+
git install https://github.com/coderigo/stata-switch
24+
local expectedRepoName = "stata-switch"
25+
local repoSnakeCase = "switch"
26+
capture confirm file "`gitDir'`expectedRepoName'"
27+
if _rc!=0{
28+
di as red "Test 1 FAILED."
29+
di as red "Expected `gitDir'`expectedRepoName' to exist but it does not."
30+
}
31+
else {
32+
di as green "Test 1 PASSED."
33+
capture confirm file "`adoPlusDir's/`repoSnakeCase'.ado"
34+
if _rc!=0{
35+
di as red "Test 2 FAILED."
36+
di "Expected `adoPlusDir's/`repoSnakeCase'.ado to exist but it does not."
37+
}
38+
else {
39+
di as green "Test 2 PASSED."
40+
}
41+
42+
/*Test uninstalling*/
43+
git uninstall `repoSnakeCase'
44+
capture confirm file "`gitDir'`expectedRepoName'"
45+
if _rc!=0{
46+
di as green "Test 3 PASSED."
47+
}
48+
else{
49+
di as red "Test 3 FAILED."
50+
di "Expected `gitDir'`expectedRepoName' to NOT exist but it does."
51+
}
52+
}
53+
54+
/* TODO: Add more tests */

0 commit comments

Comments
 (0)