Skip to content

Commit efeaba2

Browse files
committed
Add opensource git analytics example
1 parent fe29e53 commit efeaba2

14 files changed

+6328
-0
lines changed

03-git-analytics/.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

03-git-analytics/.eslintrc

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": ["@gravity-ui/eslint-config", "@gravity-ui/eslint-config/prettier"],
3+
"env": {
4+
"node": true
5+
}
6+
}

03-git-analytics/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
repo
3+
4+
database.sqlite

03-git-analytics/.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
registry=https://registry.npmjs.org

03-git-analytics/.prettierrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('@gravity-ui/prettier-config');

03-git-analytics/README.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## 03. Analyze all git repository commits and save it to SQLite or PostgreSQL
2+
3+
### Prerequisites
4+
5+
JavaScript runtime environment
6+
7+
- [Node.js](https://nodejs.org/en)
8+
9+
Git Version Control System
10+
11+
- [Git](https://git-scm.com/downloads)
12+
13+
\* Bash compatible shell for Windows
14+
- [Git Bash](https://git-scm.com/downloads)
15+
16+
### 1. Install all additional packages
17+
18+
`npm i`
19+
20+
### 2. Run analyze script
21+
22+
`npm start`
23+
24+
### 3. Import data from SQLite to PostgreSQL
25+
26+
Install `pgloader` util
27+
28+
- macOS
29+
30+
`brew install pgloader`
31+
32+
- Linux
33+
34+
`sudo apt-get install pgloader`
35+
36+
Import data from SQLite to PostgreSQL
37+
38+
```sh
39+
pgloader sqlite://./database.sqlite postgresql://username:password@hostname:port/databasename
40+
```

03-git-analytics/ext.ts

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
4+
const FILE_EXTENSIONS = [
5+
'asm',
6+
'brs',
7+
'c',
8+
'cc',
9+
'clj',
10+
'cljs',
11+
'cls',
12+
'coffee',
13+
'cpp',
14+
'cr',
15+
'cs',
16+
'css',
17+
'cxx',
18+
'erl',
19+
'fs',
20+
'fsi',
21+
'fsx',
22+
'go',
23+
'groovy',
24+
'gs',
25+
'h',
26+
'handlebars',
27+
'hbs',
28+
'hpp',
29+
'hr',
30+
'hs',
31+
'html',
32+
'htm',
33+
'hx',
34+
'hxx',
35+
'hy',
36+
'iced',
37+
'ily',
38+
'ino',
39+
'jade',
40+
'java',
41+
'jl',
42+
'js',
43+
'jsx',
44+
'mjs',
45+
'kt',
46+
'kts',
47+
'latex',
48+
'less',
49+
'ly',
50+
'lua',
51+
'ls',
52+
'ml',
53+
'mli',
54+
'mochi',
55+
'monkey',
56+
'mustache',
57+
'nix',
58+
'nim',
59+
'nut',
60+
'php',
61+
'php5',
62+
'pl',
63+
'py',
64+
'r',
65+
'rb',
66+
'rkt',
67+
'rs',
68+
'sass',
69+
'scala',
70+
'scss',
71+
'sty',
72+
'styl',
73+
'svg',
74+
'png',
75+
'sql',
76+
'swift',
77+
'tex',
78+
'ts',
79+
'tsx',
80+
'vb',
81+
'vue',
82+
'xml',
83+
'yaml',
84+
'yml',
85+
'm',
86+
'mm',
87+
'bsl',
88+
'csv',
89+
'sh',
90+
'bash',
91+
'conf',
92+
'md',
93+
'json',
94+
].map((f) => `.${f}`);
95+
96+
export const ext = async (dir: string) => {
97+
const data: Record<string, number> = {};
98+
99+
const files = await fs.promises.readdir(dir, {recursive: true});
100+
files.forEach((f) => {
101+
const e = path.extname(f);
102+
if (!FILE_EXTENSIONS.includes(e)) return;
103+
data[e] = (data[e] || 0) + 1;
104+
});
105+
106+
return data;
107+
};

03-git-analytics/git.ts

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import {shell} from './shell';
2+
3+
type Dir = {
4+
dir?: string;
5+
};
6+
7+
type Repo = Dir & {
8+
repo: string;
9+
onProgress?: (phase: string, percentage: number) => void;
10+
};
11+
12+
type Ref = Dir & {
13+
ref: string;
14+
};
15+
16+
type Pull = Dir & {
17+
ref?: string;
18+
remote?: string;
19+
};
20+
21+
type Log = {
22+
oid: string;
23+
commit: {
24+
author: {
25+
name: string;
26+
email: string;
27+
timestamp: number;
28+
};
29+
message: string;
30+
};
31+
};
32+
33+
const ProgressRegExp = new RegExp(/^(remote: )?([^:]+?):[ ]+(\d+)%/);
34+
35+
const clone = ({dir, repo, onProgress}: Repo) => {
36+
const stderr = onProgress
37+
? (chunk?: string) => {
38+
if (!chunk) return;
39+
const progress = ProgressRegExp.exec(chunk);
40+
if (progress) {
41+
onProgress(progress[2].toLowerCase(), parseInt(progress[3], 10));
42+
}
43+
}
44+
: undefined;
45+
46+
return shell.exec(`git clone --single-branch --progress [email protected]:${repo}.git ${dir}`, {
47+
cwd: process.cwd(),
48+
stderr,
49+
});
50+
};
51+
52+
const remove = ({dir, repo}: Repo) =>
53+
shell.exec(`rm -rf ${dir || repo.split('/').pop()}`, {
54+
cwd: process.cwd(),
55+
});
56+
57+
const pull = ({dir, remote, ref}: Pull = {}) =>
58+
shell.exec(`git pull ${remote} ${ref}`, {
59+
cwd: dir || process.cwd(),
60+
});
61+
62+
const main = async ({dir}: Dir = {}): Promise<string> => {
63+
const r = await shell.exec(
64+
`git branch --remote --list 'origin/*' --format '{"branch":"%(refname:short)"}'`,
65+
{
66+
cwd: dir || process.cwd(),
67+
},
68+
);
69+
if (r.stdout) {
70+
return r.stdout
71+
.split('\n')
72+
.map((line) => JSON.parse(line))
73+
.filter((line) => line.branch.startsWith('origin/'))
74+
.pop()
75+
.branch.replace(/^origin\//, '');
76+
}
77+
return '';
78+
};
79+
80+
const checkout = ({ref, dir}: Ref) =>
81+
shell.exec(`git checkout --force ${ref}`, {
82+
cwd: dir || process.cwd(),
83+
});
84+
85+
const log = async ({dir}: Dir): Promise<Array<Log>> => {
86+
const r = await shell.exec(
87+
`git log --reflog --pretty=format:'{"oid":"%H","commit":{"author":{"name":"%aN","email":"%aE","timestamp":%ad},"message": "%f"}}' --date=unix`,
88+
{
89+
cwd: dir || process.cwd(),
90+
},
91+
);
92+
if (r.stdout) {
93+
return r.stdout.split('\n').map((line) => JSON.parse(line.replace(/""(.+)""/g, '"$1"')));
94+
}
95+
return [];
96+
};
97+
98+
const clean = ({dir}: Dir = {}) =>
99+
shell.exec(`git clean -fdx`, {
100+
cwd: dir || process.cwd(),
101+
});
102+
103+
const reset = ({dir}: Dir = {}) =>
104+
shell.exec(`git reset HEAD --hard`, {
105+
cwd: dir || process.cwd(),
106+
});
107+
108+
export const git = {
109+
clone,
110+
pull,
111+
checkout,
112+
log,
113+
reset,
114+
remove,
115+
clean,
116+
main,
117+
};

0 commit comments

Comments
 (0)