Skip to content

Commit 5c19fb9

Browse files
authored
Merge pull request #31 from hpicgs/dev
Release v0.6.0
2 parents 5f5f162 + a84e992 commit 5c19fb9

File tree

19 files changed

+265
-289
lines changed

19 files changed

+265
-289
lines changed

.github/workflows/main.yml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ jobs:
1515

1616
steps:
1717
- name: Checkout 🛎️
18-
uses: 'actions/checkout@v3'
18+
uses: "actions/checkout@v3"
1919
- name: Bump version and push tag 🏷
2020
uses: anothrNick/github-tag-action@1.36.0
2121
env:
2222
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2323
WITH_V: true
24-
24+
2525
deploy:
2626
name: Deploy HiViSer Frontend to GitHub Pages 🚀
2727
concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession.
@@ -40,9 +40,17 @@ jobs:
4040
uses: actions/setup-node@v2
4141
with:
4242
node-version: 16
43-
cache: 'pnpm'
43+
cache: "pnpm"
4444
cache-dependency-path: frontend/pnpm-lock.yaml
4545

46+
- name: Install treemaps library 📦
47+
run: |
48+
cd frontend
49+
git clone --branch hiviser_deployment https://${{ secrets.SEERENE_TREEMAPS_DEPLOY_USER }}:${{ secrets.SEERENE_TREEMAPS_DEPLOY_TOKEN }}@gitlab.hpi3d.de/seerene/treemaps.git
50+
cd treemaps
51+
npm install
52+
npm run build-lib
53+
4654
- name: Install and Build 🔧
4755
run: |
4856
cd frontend

.github/workflows/pull-request.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
cd frontend
3333
git clone --branch hiviser_deployment https://${{ secrets.SEERENE_TREEMAPS_DEPLOY_USER }}:${{ secrets.SEERENE_TREEMAPS_DEPLOY_TOKEN }}@gitlab.hpi3d.de/seerene/treemaps.git
3434
cd treemaps
35-
npm i
35+
npm install
3636
npm run build-lib
3737
3838
- name: Install and Build 🔧

.github/workflows/test.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ jobs:
1919
- name: Run HiViSer Action 🚀
2020
uses: ./ # Uses the action in the root directory
2121
with:
22-
repository_path: ./testrepo
22+
benchmark: true
23+
# repository_path: ./testrepo
24+

README.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,24 @@
22
Seminar Project of the Seminar "Advanced Techniques for Analysis and Visualization of Software Data" of CGS, HPI and DEF in the Summer Term 2022
33

44
# Usage
5-
⚠ This will not work until we made our first release
6-
7-
Create a new GitHub Actions workflow in your project, e.g. at .github/workflows/hiviser.yml. The content of the file should be in the following format:
5+
Create a new GitHub Actions workflow in your project, e.g. at `.github/workflows/analytics-embedding.yml`. The content of the file should be in the following format:
86
```yaml
9-
name: HiViSer
7+
name: Analytics Treemap Embedding
108

119
on:
1210
# Trigger the workflow on push or pull request,
1311
# but only for the main branch
1412
push:
1513
branches:
1614
- main
17-
# Replace pull_request with pull_request_target if you
18-
# plan to use this action with forks, see the Limitations section
15+
1916
pull_request:
2017
branches:
2118
- main
2219

2320
jobs:
24-
run-hiviser:
25-
name: Run HiViSer
21+
analytics-embedding:
22+
name: Run Analytics Treemap Embedding 🔎
2623
runs-on: ubuntu-latest
2724

2825
steps:
@@ -31,9 +28,11 @@ jobs:
3128

3229
- name: Run Hiviser Action
3330
uses: hpicgs/hiviser-action@v0
34-
# Optional, use if you want to analyse a specific folder
3531
with:
32+
# Optional, use if you want to analyse a specific folder
3633
repository_path: ./
34+
# Optional to enable benchmarking
35+
benchmark: true
3736
```
3837
3938
## Development

action.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@ inputs:
1818
description: >
1919
The root directory of the repository.
2020
default: "."
21+
benchmark:
22+
description: >
23+
Enable benchmarking to time the different steps of the action.
24+
default: "false"
2125
runs:
2226
using: "docker"
2327
image: "Dockerfile"
2428
env:
2529
REPOSITORY_PATH: ${{ inputs.repository_path }}
30+
BENCHMARK: ${{ inputs.benchmark }}

analytics/src/github.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ async function createBlob(content: string) {
7171
console.log(response);
7272
}
7373

74-
async function createTree(csv: string, json: string): Promise<string> {
74+
async function createTree(csv: string): Promise<string> {
7575
console.log(`creating tree at ${owner}/${repo}`);
7676

7777
const response = await octokit.request(
@@ -86,12 +86,6 @@ async function createTree(csv: string, json: string): Promise<string> {
8686
type: "blob",
8787
content: csv,
8888
},
89-
{
90-
path: "metrics.json",
91-
mode: "100644",
92-
type: "blob",
93-
content: json,
94-
},
9589
],
9690
}
9791
);
@@ -100,15 +94,12 @@ async function createTree(csv: string, json: string): Promise<string> {
10094
return response.data.sha;
10195
}
10296

103-
export async function storeMetricsToRepo(
104-
metrics_csv: string,
105-
metrics_json: string
106-
) {
97+
export async function storeMetricsToRepo(metrics_csv: string) {
10798
if (process.env.DEBUG) {
10899
console.log("DEBUG mode enabled, skipping GitHub API calls");
109100
return;
110101
}
111102

112-
const tree_sha = await createTree(metrics_csv, metrics_json);
103+
const tree_sha = await createTree(metrics_csv);
113104
await createRef(`refs/metrics/${commit_sha}`, tree_sha);
114105
}

analytics/src/index.ts

Lines changed: 38 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
1-
import { lstatSync, readdirSync, Stats, statSync, writeFileSync } from "fs";
2-
import { promisify } from "util";
31
import glob from "glob";
2+
import { writeFileSync } from "fs";
3+
import { promisify } from "util";
44
import { storeMetricsToRepo } from "./github";
5-
import { basename, extname, join } from "path";
65
import { createObjectCsvStringifier } from "csv-writer";
76
import { getMetrics } from "./metrics";
8-
import {
9-
DirectoryNode,
10-
FileNode,
11-
Metrics,
12-
MetricsNode,
13-
NodeType,
14-
} from "./types";
7+
import { performance } from "perf_hooks";
158

169
const globPromise = promisify(glob);
1710

18-
async function analyseRepository() {
11+
async function analyseRepository(benchmark = false) {
1912
if (!process.env.REPOSITORY_PATH)
2013
throw new Error("REPOSITORY_PATH environment variable is not set");
2114
console.log(`REPOSITORY_PATH="${process.env.REPOSITORY_PATH}"`);
@@ -26,130 +19,49 @@ async function analyseRepository() {
2619
const csvStringifier = createObjectCsvStringifier({
2720
header: [
2821
{ id: "filename", title: "filename" },
29-
{ id: "loc", title: "Line of Code" },
30-
{ id: "noc", title: "Number of Comments" },
31-
{ id: "cloc", title: "Commented Lines of Code" },
32-
{ id: "dc", title: "Density of Comments" },
33-
{ id: "nof", title: "Number of Functions" },
22+
{ id: "loc", title: "loc" }, // Lines of Code
23+
{ id: "noc", title: "noc" }, // Number of Comments
24+
{ id: "cloc", title: "cloc" }, //Commented Lines of Code
25+
{ id: "dc", title: "dc" }, // Density of Comments
26+
{ id: "nof", title: "nof" }, // Number of Functions
3427
],
3528
});
3629

37-
const metrics = files.map((filename) => ({
38-
filename,
39-
...getMetrics(filename),
40-
}));
30+
let timings: { [filename: string]: number } = {};
31+
if (benchmark) console.time("calculating-metrics");
32+
const metrics = files.map((filename) => {
33+
if (benchmark) {
34+
const start = performance.now();
35+
const m = {
36+
filename,
37+
...getMetrics(filename),
38+
};
39+
const end = performance.now();
40+
timings[filename] = end - start;
41+
return m;
42+
} else {
43+
return {
44+
filename,
45+
...getMetrics(filename),
46+
};
47+
}
48+
});
49+
if (benchmark) console.timeEnd("calculating-metrics");
50+
if (benchmark) console.log(timings);
51+
if (benchmark) console.time("writing-csv");
52+
4153
const header = csvStringifier.getHeaderString();
4254
const csv = header + csvStringifier.stringifyRecords(metrics);
43-
const metricsTree = parseRepositoryTree(process.env.REPOSITORY_PATH);
44-
const json = JSON.stringify(metricsTree, null, 2);
45-
4655
writeFileSync("metrics.csv", csv);
47-
writeFileSync("metrics.json", json);
48-
49-
storeMetricsToRepo(csv, json);
50-
}
51-
52-
// Adopted from https://github.com/mihneadb/node-directory-tree/blob/master/lib/directory-tree.js#L21
53-
function safeReadDirSync(path: string) {
54-
let dirData = [];
55-
try {
56-
dirData = readdirSync(path);
57-
} catch (ex: any) {
58-
if (ex.code == "EACCES" || ex.code == "EPERM") {
59-
//User does not have permissions, ignore directory
60-
return null;
61-
} else throw ex;
62-
}
63-
return dirData;
64-
}
65-
66-
function reduceMetrics(children: MetricsNode[]): Metrics {
67-
const metricsList = children.reduce(
68-
(result: Metrics[], element: MetricsNode) => {
69-
if (element.metrics) {
70-
result.push(element.metrics);
71-
}
72-
return result;
73-
},
74-
[]
75-
);
7656

77-
return metricsList.reduce(
78-
(acc, cur) => ({
79-
loc: acc.loc + cur.loc,
80-
noc: acc.noc + cur.noc,
81-
cloc: acc.cloc + cur.cloc,
82-
//Todo this should not be accumulated
83-
dc: acc.dc + cur.dc,
84-
nof: acc.nof + cur.nof,
85-
}),
86-
{ loc: 0, noc: 0, cloc: 0, dc: 0, nof: 0 }
87-
);
88-
}
89-
90-
function parseRepositoryTree(
91-
path: string,
92-
currentDepth = 0,
93-
maxDepth: number | undefined = undefined
94-
): MetricsNode | null {
95-
const extensions = /\.ts$/;
96-
const name = basename(path);
97-
98-
let stats: Stats;
99-
let lstats: Stats;
100-
101-
try {
102-
stats = statSync(path);
103-
lstats = lstatSync(path);
104-
} catch (e) {
105-
return null;
106-
}
107-
108-
if (lstats.isSymbolicLink()) {
109-
// Skip symbolic links
110-
return null;
111-
}
112-
113-
if (stats.isFile()) {
114-
const ext = extname(path).toLowerCase();
115-
116-
// Skip if it does not match the extension regex
117-
if (extensions && !extensions.test(ext)) return null;
118-
const fileNode = {
119-
path,
120-
name,
121-
type: NodeType.FILE,
122-
metrics: getMetrics(path),
123-
} as FileNode;
124-
return fileNode;
125-
} else if (stats.isDirectory()) {
126-
const dirNode = { path, name, type: NodeType.DIRECTORY } as DirectoryNode;
127-
let dirData = safeReadDirSync(path);
128-
if (dirData === null) return null;
129-
130-
if (maxDepth === undefined || maxDepth > currentDepth) {
131-
dirNode.children = dirData.reduce(
132-
(children: MetricsNode[], childPath: string) => {
133-
const child = parseRepositoryTree(
134-
join(path, childPath),
135-
currentDepth + 1
136-
);
137-
if (child) {
138-
children.push(child);
139-
}
140-
return children;
141-
},
142-
[]
143-
);
144-
145-
dirNode.metrics = reduceMetrics(dirNode.children);
146-
}
147-
return dirNode;
148-
}
149-
return null;
57+
if (benchmark) console.timeEnd("writing-csv");
58+
if (benchmark) console.time("storing-in-github");
59+
await storeMetricsToRepo(csv);
60+
if (benchmark) console.timeEnd("storing-in-github");
15061
}
15162

152-
analyseRepository().catch((e) => {
63+
const benchmark = Boolean(process.env.BENCHMARK);
64+
analyseRepository(benchmark).catch((e) => {
15365
console.error(e);
15466
process.exit(1);
15567
});

analytics/src/types.ts

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
1-
export interface FileMetrics {
2-
[key: string]: any;
3-
}
4-
5-
export interface MetricsTableData {
6-
header: string[];
7-
rows: FileMetrics[];
8-
}
9-
10-
export enum NodeType {
11-
FILE = "file",
12-
DIRECTORY = "directory",
1+
export interface FileMetrics extends Metrics {
2+
filename: string;
133
}
144

155
export interface Metrics {
@@ -19,19 +9,17 @@ export interface Metrics {
199
dc: number;
2010
nof: number;
2111
}
22-
23-
export interface MetricsNode {
24-
path: string;
25-
name: string;
26-
type: NodeType;
27-
metrics: Metrics;
12+
export interface MetricsTableData {
13+
header: string[];
14+
rows: FileMetrics[];
2815
}
2916

30-
export interface DirectoryNode extends MetricsNode {
31-
type: NodeType.DIRECTORY;
32-
children: MetricsNode[];
17+
export interface FileTree {
18+
root: TreeNode;
3319
}
3420

35-
export interface FileNode extends MetricsNode {
36-
type: NodeType.FILE;
21+
export interface TreeNode {
22+
name: string;
23+
children: TreeNode[];
24+
metrics?: Metrics;
3725
}

0 commit comments

Comments
 (0)