korekt-cli 0.8.3 โ 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/formatter.js +2 -0
- package/src/git-logic.js +29 -0
- package/src/git-logic.test.js +343 -0
- package/src/index.js +4 -55
- package/src/utils.js +54 -0
package/package.json
CHANGED
package/src/formatter.js
CHANGED
package/src/git-logic.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execa } from 'execa';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
+
import { detectCIProvider } from './utils.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Truncate content to a maximum number of lines using "head and tail".
|
|
@@ -98,6 +99,30 @@ export function shouldIgnoreFile(filePath, patterns) {
|
|
|
98
99
|
return false;
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Calculate total changed lines (additions + deletions) from changed files
|
|
104
|
+
* @param {Array} changedFiles - Array of file objects with diff property
|
|
105
|
+
* @returns {number} - Total number of changed lines
|
|
106
|
+
*/
|
|
107
|
+
export function calculateChangedLines(changedFiles) {
|
|
108
|
+
let changedLines = 0;
|
|
109
|
+
for (const file of changedFiles) {
|
|
110
|
+
if (file.diff) {
|
|
111
|
+
const lines = file.diff.split('\n');
|
|
112
|
+
for (const line of lines) {
|
|
113
|
+
if (
|
|
114
|
+
(line.startsWith('+') || line.startsWith('-')) &&
|
|
115
|
+
!line.startsWith('+++') &&
|
|
116
|
+
!line.startsWith('---')
|
|
117
|
+
) {
|
|
118
|
+
changedLines++;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return changedLines;
|
|
124
|
+
}
|
|
125
|
+
|
|
101
126
|
/**
|
|
102
127
|
* Helper function to parse the complex output of git diff --name-status
|
|
103
128
|
*/
|
|
@@ -219,6 +244,8 @@ export async function runUncommittedReview(mode = 'unstaged') {
|
|
|
219
244
|
commit_messages: [], // No commits for uncommitted changes
|
|
220
245
|
changed_files: changedFiles,
|
|
221
246
|
source_branch: branchName,
|
|
247
|
+
changed_lines: calculateChangedLines(changedFiles),
|
|
248
|
+
is_ci: detectCIProvider() !== null,
|
|
222
249
|
};
|
|
223
250
|
} catch (error) {
|
|
224
251
|
console.error(chalk.red('Failed to analyze uncommitted changes:'), error.message);
|
|
@@ -498,6 +525,8 @@ export async function runLocalReview(targetBranch = null, ignorePatterns = null)
|
|
|
498
525
|
author_email,
|
|
499
526
|
author_name,
|
|
500
527
|
contributors,
|
|
528
|
+
changed_lines: calculateChangedLines(changedFiles),
|
|
529
|
+
is_ci: detectCIProvider() !== null,
|
|
501
530
|
};
|
|
502
531
|
} catch (error) {
|
|
503
532
|
console.error(chalk.red('Failed to run local review analysis:'), error.message);
|
package/src/git-logic.test.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getContributors,
|
|
10
10
|
} from './git-logic.js';
|
|
11
11
|
import { execa } from 'execa';
|
|
12
|
+
import { detectCIProvider } from './utils.js';
|
|
12
13
|
|
|
13
14
|
describe('parseNameStatus', () => {
|
|
14
15
|
it('should correctly parse M, A, and D statuses', () => {
|
|
@@ -667,3 +668,345 @@ describe('getContributors', () => {
|
|
|
667
668
|
expect(result.contributors[2].commits).toBe(1);
|
|
668
669
|
});
|
|
669
670
|
});
|
|
671
|
+
|
|
672
|
+
describe('changed_lines calculation', () => {
|
|
673
|
+
beforeEach(() => {
|
|
674
|
+
vi.mock('execa');
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
afterEach(() => {
|
|
678
|
+
vi.restoreAllMocks();
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
it('should calculate changed_lines for uncommitted changes', async () => {
|
|
682
|
+
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
683
|
+
const command = [cmd, ...args].join(' ');
|
|
684
|
+
|
|
685
|
+
if (command.includes('remote get-url origin')) {
|
|
686
|
+
return { stdout: 'https://github.com/user/repo.git' };
|
|
687
|
+
}
|
|
688
|
+
if (command.includes('rev-parse --abbrev-ref HEAD')) {
|
|
689
|
+
return { stdout: 'feature-branch' };
|
|
690
|
+
}
|
|
691
|
+
if (command.includes('rev-parse --show-toplevel')) {
|
|
692
|
+
return { stdout: '/fake/repo/path' };
|
|
693
|
+
}
|
|
694
|
+
if (command.includes('diff --cached --name-status')) {
|
|
695
|
+
return { stdout: 'M\tfile.js' };
|
|
696
|
+
}
|
|
697
|
+
if (command.includes('diff --cached -U15 -- file.js')) {
|
|
698
|
+
// Simulate a diff with 5 additions and 3 deletions (8 total changed lines)
|
|
699
|
+
return {
|
|
700
|
+
stdout:
|
|
701
|
+
'diff --git a/file.js b/file.js\n' +
|
|
702
|
+
'--- a/file.js\n' +
|
|
703
|
+
'+++ b/file.js\n' +
|
|
704
|
+
'@@ -1,5 +1,10 @@\n' +
|
|
705
|
+
'+new line 1\n' +
|
|
706
|
+
'+new line 2\n' +
|
|
707
|
+
' existing line\n' +
|
|
708
|
+
'-old line 1\n' +
|
|
709
|
+
'-old line 2\n' +
|
|
710
|
+
'-old line 3\n' +
|
|
711
|
+
'+new line 3\n' +
|
|
712
|
+
'+new line 4\n' +
|
|
713
|
+
'+new line 5\n',
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
if (command.includes('show HEAD:file.js')) {
|
|
717
|
+
return { stdout: 'old content' };
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
throw new Error(`Unmocked command: ${command}`);
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
const result = await runUncommittedReview('staged');
|
|
724
|
+
|
|
725
|
+
expect(result).toBeDefined();
|
|
726
|
+
expect(result.changed_lines).toBe(8); // 5 additions + 3 deletions
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
it('should calculate changed_lines for local review', async () => {
|
|
730
|
+
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
731
|
+
const command = [cmd, ...args].join(' ');
|
|
732
|
+
|
|
733
|
+
if (command.includes('remote get-url origin')) {
|
|
734
|
+
return { stdout: 'https://github.com/user/repo.git' };
|
|
735
|
+
}
|
|
736
|
+
if (command.includes('rev-parse --abbrev-ref HEAD')) {
|
|
737
|
+
return { stdout: 'feature-branch' };
|
|
738
|
+
}
|
|
739
|
+
if (command.includes('rev-parse --show-toplevel')) {
|
|
740
|
+
return { stdout: '/path/to/repo' };
|
|
741
|
+
}
|
|
742
|
+
if (command.includes('rev-parse --verify main')) {
|
|
743
|
+
return { stdout: 'commit-hash' };
|
|
744
|
+
}
|
|
745
|
+
if (command === 'git fetch origin main') {
|
|
746
|
+
return { stdout: '' };
|
|
747
|
+
}
|
|
748
|
+
if (command.includes('merge-base origin/main HEAD')) {
|
|
749
|
+
return { stdout: 'abc123' };
|
|
750
|
+
}
|
|
751
|
+
if (command.includes('log --no-merges --pretty=%B---EOC---')) {
|
|
752
|
+
return { stdout: 'feat: add feature---EOC---' };
|
|
753
|
+
}
|
|
754
|
+
if (command.includes('diff --name-status')) {
|
|
755
|
+
return { stdout: 'M\tfile1.js\nA\tfile2.js' };
|
|
756
|
+
}
|
|
757
|
+
if (command.includes('diff -U15') && command.includes('file1.js')) {
|
|
758
|
+
// 10 lines changed in file1.js
|
|
759
|
+
return {
|
|
760
|
+
stdout:
|
|
761
|
+
'diff --git a/file1.js b/file1.js\n' +
|
|
762
|
+
'+line1\n' +
|
|
763
|
+
'+line2\n' +
|
|
764
|
+
'-line3\n' +
|
|
765
|
+
'-line4\n' +
|
|
766
|
+
'+line5\n' +
|
|
767
|
+
'+line6\n' +
|
|
768
|
+
'+line7\n' +
|
|
769
|
+
'-line8\n' +
|
|
770
|
+
'+line9\n' +
|
|
771
|
+
'+line10\n',
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
if (command.includes('diff -U15') && command.includes('file2.js')) {
|
|
775
|
+
// 5 lines added in file2.js (new file)
|
|
776
|
+
return {
|
|
777
|
+
stdout:
|
|
778
|
+
'diff --git a/file2.js b/file2.js\n' +
|
|
779
|
+
'+line1\n' +
|
|
780
|
+
'+line2\n' +
|
|
781
|
+
'+line3\n' +
|
|
782
|
+
'+line4\n' +
|
|
783
|
+
'+line5\n',
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
if (command.includes('show abc123:file1.js')) {
|
|
787
|
+
return { stdout: 'original content' };
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return { stdout: '' };
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
const result = await runLocalReview('main');
|
|
794
|
+
|
|
795
|
+
expect(result).toBeDefined();
|
|
796
|
+
expect(result.changed_lines).toBe(15); // 10 from file1.js + 5 from file2.js
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
it('should handle diffs with no changes', async () => {
|
|
800
|
+
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
801
|
+
const command = [cmd, ...args].join(' ');
|
|
802
|
+
|
|
803
|
+
if (command.includes('remote get-url origin')) {
|
|
804
|
+
return { stdout: 'https://github.com/user/repo.git' };
|
|
805
|
+
}
|
|
806
|
+
if (command.includes('rev-parse --abbrev-ref HEAD')) {
|
|
807
|
+
return { stdout: 'feature-branch' };
|
|
808
|
+
}
|
|
809
|
+
if (command.includes('rev-parse --show-toplevel')) {
|
|
810
|
+
return { stdout: '/fake/repo/path' };
|
|
811
|
+
}
|
|
812
|
+
if (command.includes('diff --cached --name-status')) {
|
|
813
|
+
return { stdout: 'M\tfile.js' };
|
|
814
|
+
}
|
|
815
|
+
if (command.includes('diff --cached -U15 -- file.js')) {
|
|
816
|
+
// Empty diff (no actual changes, just headers)
|
|
817
|
+
return {
|
|
818
|
+
stdout: 'diff --git a/file.js b/file.js\n--- a/file.js\n+++ b/file.js\n',
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
if (command.includes('show HEAD:file.js')) {
|
|
822
|
+
return { stdout: 'content' };
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
throw new Error(`Unmocked command: ${command}`);
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
const result = await runUncommittedReview('staged');
|
|
829
|
+
|
|
830
|
+
expect(result).toBeDefined();
|
|
831
|
+
expect(result.changed_lines).toBe(0);
|
|
832
|
+
});
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
describe('is_ci flag in payload', () => {
|
|
836
|
+
beforeEach(() => {
|
|
837
|
+
vi.mock('execa');
|
|
838
|
+
vi.mock('./utils.js', () => ({
|
|
839
|
+
detectCIProvider: vi.fn(),
|
|
840
|
+
}));
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
afterEach(() => {
|
|
844
|
+
vi.restoreAllMocks();
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
it('should set is_ci to true when detectCIProvider returns a provider', async () => {
|
|
848
|
+
vi.mocked(detectCIProvider).mockReturnValue('github');
|
|
849
|
+
|
|
850
|
+
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
851
|
+
const command = [cmd, ...args].join(' ');
|
|
852
|
+
|
|
853
|
+
if (command.includes('remote get-url origin')) {
|
|
854
|
+
return { stdout: 'https://github.com/user/repo.git' };
|
|
855
|
+
}
|
|
856
|
+
if (command.includes('rev-parse --abbrev-ref HEAD')) {
|
|
857
|
+
return { stdout: 'feature-branch' };
|
|
858
|
+
}
|
|
859
|
+
if (command.includes('rev-parse --show-toplevel')) {
|
|
860
|
+
return { stdout: '/fake/repo/path' };
|
|
861
|
+
}
|
|
862
|
+
if (command.includes('diff --cached --name-status')) {
|
|
863
|
+
return { stdout: 'M\tfile.js' };
|
|
864
|
+
}
|
|
865
|
+
if (command.includes('diff --cached -U15 -- file.js')) {
|
|
866
|
+
return { stdout: 'diff --git a/file.js b/file.js\n+new line' };
|
|
867
|
+
}
|
|
868
|
+
if (command.includes('show HEAD:file.js')) {
|
|
869
|
+
return { stdout: 'old content' };
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
throw new Error(`Unmocked command: ${command}`);
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
const result = await runUncommittedReview('staged');
|
|
876
|
+
|
|
877
|
+
expect(result).toBeDefined();
|
|
878
|
+
expect(result.is_ci).toBe(true);
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
it('should set is_ci to false when detectCIProvider returns null', async () => {
|
|
882
|
+
vi.mocked(detectCIProvider).mockReturnValue(null);
|
|
883
|
+
|
|
884
|
+
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
885
|
+
const command = [cmd, ...args].join(' ');
|
|
886
|
+
|
|
887
|
+
if (command.includes('remote get-url origin')) {
|
|
888
|
+
return { stdout: 'https://github.com/user/repo.git' };
|
|
889
|
+
}
|
|
890
|
+
if (command.includes('rev-parse --abbrev-ref HEAD')) {
|
|
891
|
+
return { stdout: 'feature-branch' };
|
|
892
|
+
}
|
|
893
|
+
if (command.includes('rev-parse --show-toplevel')) {
|
|
894
|
+
return { stdout: '/fake/repo/path' };
|
|
895
|
+
}
|
|
896
|
+
if (command.includes('diff --cached --name-status')) {
|
|
897
|
+
return { stdout: 'M\tfile.js' };
|
|
898
|
+
}
|
|
899
|
+
if (command.includes('diff --cached -U15 -- file.js')) {
|
|
900
|
+
return { stdout: 'diff --git a/file.js b/file.js\n+new line' };
|
|
901
|
+
}
|
|
902
|
+
if (command.includes('show HEAD:file.js')) {
|
|
903
|
+
return { stdout: 'old content' };
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
throw new Error(`Unmocked command: ${command}`);
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
const result = await runUncommittedReview('staged');
|
|
910
|
+
|
|
911
|
+
expect(result).toBeDefined();
|
|
912
|
+
expect(result.is_ci).toBe(false);
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
it('should include is_ci in runLocalReview payload', async () => {
|
|
916
|
+
vi.mocked(detectCIProvider).mockReturnValue('azure');
|
|
917
|
+
|
|
918
|
+
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
919
|
+
const command = [cmd, ...args].join(' ');
|
|
920
|
+
|
|
921
|
+
if (command.includes('remote get-url origin')) {
|
|
922
|
+
return { stdout: 'https://github.com/user/repo.git' };
|
|
923
|
+
}
|
|
924
|
+
if (command.includes('rev-parse --abbrev-ref HEAD')) {
|
|
925
|
+
return { stdout: 'feature-branch' };
|
|
926
|
+
}
|
|
927
|
+
if (command.includes('rev-parse --show-toplevel')) {
|
|
928
|
+
return { stdout: '/path/to/repo' };
|
|
929
|
+
}
|
|
930
|
+
if (command.includes('rev-parse --verify main')) {
|
|
931
|
+
return { stdout: 'commit-hash' };
|
|
932
|
+
}
|
|
933
|
+
if (command === 'git fetch origin main') {
|
|
934
|
+
return { stdout: '' };
|
|
935
|
+
}
|
|
936
|
+
if (command.includes('merge-base origin/main HEAD')) {
|
|
937
|
+
return { stdout: 'abc123' };
|
|
938
|
+
}
|
|
939
|
+
if (command.includes('log --no-merges --pretty=%B---EOC---')) {
|
|
940
|
+
return { stdout: 'feat: add feature---EOC---' };
|
|
941
|
+
}
|
|
942
|
+
if (command.includes('log --no-merges --format=%ae|%an')) {
|
|
943
|
+
return { stdout: 'user@example.com|User Name' };
|
|
944
|
+
}
|
|
945
|
+
if (command.includes('diff --name-status')) {
|
|
946
|
+
return { stdout: 'M\tfile.js' };
|
|
947
|
+
}
|
|
948
|
+
if (command.includes('diff -U15')) {
|
|
949
|
+
return { stdout: 'diff content' };
|
|
950
|
+
}
|
|
951
|
+
if (command.includes('show abc123:file.js')) {
|
|
952
|
+
return { stdout: 'original content' };
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
return { stdout: '' };
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
const result = await runLocalReview('main');
|
|
959
|
+
|
|
960
|
+
expect(result).toBeDefined();
|
|
961
|
+
expect(result.is_ci).toBe(true);
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
it('should set is_ci to false in runLocalReview when not in CI', async () => {
|
|
965
|
+
vi.mocked(detectCIProvider).mockReturnValue(null);
|
|
966
|
+
|
|
967
|
+
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
968
|
+
const command = [cmd, ...args].join(' ');
|
|
969
|
+
|
|
970
|
+
if (command.includes('remote get-url origin')) {
|
|
971
|
+
return { stdout: 'https://github.com/user/repo.git' };
|
|
972
|
+
}
|
|
973
|
+
if (command.includes('rev-parse --abbrev-ref HEAD')) {
|
|
974
|
+
return { stdout: 'feature-branch' };
|
|
975
|
+
}
|
|
976
|
+
if (command.includes('rev-parse --show-toplevel')) {
|
|
977
|
+
return { stdout: '/path/to/repo' };
|
|
978
|
+
}
|
|
979
|
+
if (command.includes('rev-parse --verify main')) {
|
|
980
|
+
return { stdout: 'commit-hash' };
|
|
981
|
+
}
|
|
982
|
+
if (command === 'git fetch origin main') {
|
|
983
|
+
return { stdout: '' };
|
|
984
|
+
}
|
|
985
|
+
if (command.includes('merge-base origin/main HEAD')) {
|
|
986
|
+
return { stdout: 'abc123' };
|
|
987
|
+
}
|
|
988
|
+
if (command.includes('log --no-merges --pretty=%B---EOC---')) {
|
|
989
|
+
return { stdout: 'feat: add feature---EOC---' };
|
|
990
|
+
}
|
|
991
|
+
if (command.includes('log --no-merges --format=%ae|%an')) {
|
|
992
|
+
return { stdout: 'user@example.com|User Name' };
|
|
993
|
+
}
|
|
994
|
+
if (command.includes('diff --name-status')) {
|
|
995
|
+
return { stdout: 'M\tfile.js' };
|
|
996
|
+
}
|
|
997
|
+
if (command.includes('diff -U15')) {
|
|
998
|
+
return { stdout: 'diff content' };
|
|
999
|
+
}
|
|
1000
|
+
if (command.includes('show abc123:file.js')) {
|
|
1001
|
+
return { stdout: 'original content' };
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
return { stdout: '' };
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
const result = await runLocalReview('main');
|
|
1008
|
+
|
|
1009
|
+
expect(result).toBeDefined();
|
|
1010
|
+
expect(result.is_ci).toBe(false);
|
|
1011
|
+
});
|
|
1012
|
+
});
|
package/src/index.js
CHANGED
|
@@ -14,6 +14,10 @@ import { tmpdir } from 'os';
|
|
|
14
14
|
import { runLocalReview } from './git-logic.js';
|
|
15
15
|
import { getApiKey, setApiKey, getApiEndpoint, setApiEndpoint } from './config.js';
|
|
16
16
|
import { formatReviewOutput } from './formatter.js';
|
|
17
|
+
import { detectCIProvider, truncateFileData, formatErrorOutput } from './utils.js';
|
|
18
|
+
|
|
19
|
+
// Re-export utilities for backward compatibility
|
|
20
|
+
export { detectCIProvider, truncateFileData, formatErrorOutput } from './utils.js';
|
|
17
21
|
|
|
18
22
|
const require = createRequire(import.meta.url);
|
|
19
23
|
const { version } = require('../package.json');
|
|
@@ -26,44 +30,6 @@ const { version } = require('../package.json');
|
|
|
26
30
|
const log = (msg) => process.stderr.write(msg + '\n');
|
|
27
31
|
const output = (msg) => process.stdout.write(msg + '\n');
|
|
28
32
|
|
|
29
|
-
/**
|
|
30
|
-
* Truncates file data (diff and content) for display purposes
|
|
31
|
-
* @param {Object} file - File object with path, status, diff, content, etc.
|
|
32
|
-
* @param {number} maxLength - Maximum length before truncation (default: 500)
|
|
33
|
-
* @returns {Object} File object with truncated diff and content
|
|
34
|
-
*/
|
|
35
|
-
export function truncateFileData(file, maxLength = 500) {
|
|
36
|
-
return {
|
|
37
|
-
path: file.path,
|
|
38
|
-
status: file.status,
|
|
39
|
-
...(file.old_path && { old_path: file.old_path }),
|
|
40
|
-
diff:
|
|
41
|
-
file.diff.length > maxLength
|
|
42
|
-
? `${file.diff.substring(0, maxLength)}... [truncated ${file.diff.length - maxLength} chars]`
|
|
43
|
-
: file.diff,
|
|
44
|
-
content:
|
|
45
|
-
file.content.length > maxLength
|
|
46
|
-
? `${file.content.substring(0, maxLength)}... [truncated ${file.content.length - maxLength} chars]`
|
|
47
|
-
: file.content,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Formats error object for JSON output
|
|
53
|
-
* @param {Error} error - Error object from axios or other source
|
|
54
|
-
* @returns {Object} Formatted error output with success: false
|
|
55
|
-
*/
|
|
56
|
-
export function formatErrorOutput(error) {
|
|
57
|
-
return {
|
|
58
|
-
success: false,
|
|
59
|
-
error: error.message,
|
|
60
|
-
...(error.response && {
|
|
61
|
-
status: error.response.status,
|
|
62
|
-
data: error.response.data,
|
|
63
|
-
}),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
33
|
/**
|
|
68
34
|
* Ask for user confirmation before proceeding
|
|
69
35
|
*/
|
|
@@ -84,23 +50,6 @@ async function confirmAction(message) {
|
|
|
84
50
|
});
|
|
85
51
|
}
|
|
86
52
|
|
|
87
|
-
/**
|
|
88
|
-
* Detect CI provider from environment variables
|
|
89
|
-
* @returns {string|null} Provider name or null if not detected
|
|
90
|
-
*/
|
|
91
|
-
export function detectCIProvider() {
|
|
92
|
-
if (process.env.GITHUB_TOKEN && process.env.GITHUB_REPOSITORY) {
|
|
93
|
-
return 'github';
|
|
94
|
-
}
|
|
95
|
-
if (process.env.SYSTEM_ACCESSTOKEN && process.env.SYSTEM_PULLREQUEST_PULLREQUESTID) {
|
|
96
|
-
return 'azure';
|
|
97
|
-
}
|
|
98
|
-
if (process.env.BITBUCKET_REPO_SLUG && process.env.BITBUCKET_PR_ID) {
|
|
99
|
-
return 'bitbucket';
|
|
100
|
-
}
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
53
|
/**
|
|
105
54
|
* Run the CI integration script to post comments
|
|
106
55
|
* @param {string} provider - CI provider (github, azure, bitbucket)
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect CI provider from environment variables
|
|
3
|
+
* @returns {string|null} Provider name or null if not detected
|
|
4
|
+
*/
|
|
5
|
+
export function detectCIProvider() {
|
|
6
|
+
if (process.env.GITHUB_TOKEN && process.env.GITHUB_REPOSITORY) {
|
|
7
|
+
return 'github';
|
|
8
|
+
}
|
|
9
|
+
if (process.env.SYSTEM_ACCESSTOKEN && process.env.SYSTEM_PULLREQUEST_PULLREQUESTID) {
|
|
10
|
+
return 'azure';
|
|
11
|
+
}
|
|
12
|
+
if (process.env.BITBUCKET_REPO_SLUG && process.env.BITBUCKET_PR_ID) {
|
|
13
|
+
return 'bitbucket';
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Truncates file data (diff and content) for display purposes
|
|
20
|
+
* @param {Object} file - File object with path, status, diff, content, etc.
|
|
21
|
+
* @param {number} maxLength - Maximum length before truncation (default: 500)
|
|
22
|
+
* @returns {Object} File object with truncated diff and content
|
|
23
|
+
*/
|
|
24
|
+
export function truncateFileData(file, maxLength = 500) {
|
|
25
|
+
return {
|
|
26
|
+
path: file.path,
|
|
27
|
+
status: file.status,
|
|
28
|
+
...(file.old_path && { old_path: file.old_path }),
|
|
29
|
+
diff:
|
|
30
|
+
file.diff.length > maxLength
|
|
31
|
+
? `${file.diff.substring(0, maxLength)}... [truncated ${file.diff.length - maxLength} chars]`
|
|
32
|
+
: file.diff,
|
|
33
|
+
content:
|
|
34
|
+
file.content.length > maxLength
|
|
35
|
+
? `${file.content.substring(0, maxLength)}... [truncated ${file.content.length - maxLength} chars]`
|
|
36
|
+
: file.content,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Formats error object for JSON output
|
|
42
|
+
* @param {Error} error - Error object from axios or other source
|
|
43
|
+
* @returns {Object} Formatted error output with success: false
|
|
44
|
+
*/
|
|
45
|
+
export function formatErrorOutput(error) {
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
error: error.message,
|
|
49
|
+
...(error.response && {
|
|
50
|
+
status: error.response.status,
|
|
51
|
+
data: error.response.data,
|
|
52
|
+
}),
|
|
53
|
+
};
|
|
54
|
+
}
|