1+ // SPDX-License-Identifier: MIT
2+ /*
3+ Copyright (c) 2025, SCANOSS
4+
5+ Permission is hereby granted, free of charge, to any person obtaining a copy
6+ of this software and associated documentation files (the "Software"), to deal
7+ in the Software without restriction, including without limitation the rights
8+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+ copies of the Software, and to permit persons to whom the Software is
10+ furnished to do so, subject to the following conditions:
11+
12+ The above copyright notice and this permission notice shall be included in
13+ all copies or substantial portions of the Software.
14+
15+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+ THE SOFTWARE.
22+ */
23+
24+ import * as tl from 'azure-pipelines-task-lib' ;
25+ import { EXECUTABLE , OUTPUT_FILEPATH , REPO_DIR , RUNTIME_CONTAINER } from "../app.input" ;
26+ import { CYCLONEDX_FILE_NAME , SPDXLITE_FILE_NAME , CSV_FILE_NAME } from "../app.output" ;
27+ import fs from 'fs' ;
28+ import path from "path" ;
29+
30+ export class ScanOssService {
31+ /**
32+ * Build scanoss-py conversion parameters */
33+ private buildReformatParameters ( format : string , filename : string ) : string [ ] {
34+ return [
35+ 'run' ,
36+ '-v' ,
37+ `${ REPO_DIR } :/scanoss` ,
38+ RUNTIME_CONTAINER ,
39+ 'convert' ,
40+ '--input' ,
41+ `./${ OUTPUT_FILEPATH } ` ,
42+ '--format' ,
43+ `${ format } ` ,
44+ '--output' ,
45+ `./${ filename } `
46+ ] ;
47+ }
48+
49+ /**
50+ * Reformats SCANOSS results using scanoss-py.
51+ * Currently always generates CycloneDX, SPDXLite and CSV files to be
52+ * uploaded as an artifact for other integrations.
53+ */
54+ async reformatScanResults ( format : string ) : Promise < Error | undefined > {
55+ try {
56+ console . log ( `Converting SCANOSS results to ${ format } format...` ) ;
57+ const options = {
58+ failOnStdErr : false ,
59+ ignoreReturnCode : false
60+ } ;
61+ const filename =
62+ format === 'cyclonedx'
63+ ? CYCLONEDX_FILE_NAME
64+ : format === 'spdxlite'
65+ ? SPDXLITE_FILE_NAME
66+ : format === 'csv'
67+ ? CSV_FILE_NAME
68+ : undefined ;
69+ if ( ! filename ) {
70+ return new Error ( `Unknown format: ${ format } ` ) ;
71+ }
72+ const exitCode = await tl . execAsync (
73+ EXECUTABLE ,
74+ this . buildReformatParameters ( format , filename ) ,
75+ options
76+ ) ;
77+ if ( exitCode !== 0 ) {
78+ return new Error ( `Error converting scan results into ${ format } format` ) ;
79+ }
80+ // Check if reformatted file was actually created before trying to upload it
81+ try {
82+ const formatAbsolutePath = path . join ( process . cwd ( ) , filename )
83+ await fs . promises . access ( formatAbsolutePath , fs . constants . F_OK ) ;
84+ this . uploadToArtifacts ( formatAbsolutePath ) ;
85+ console . log ( `Successfully converted results into ${ format } format` ) ;
86+ } catch ( fileError ) {
87+ // File doesn't exist - this can happen with empty repos
88+ console . log ( `${ format } conversion completed but no file generated (likely empty repository)` ) ;
89+ }
90+ } catch ( e : any ) {
91+ tl . error ( e . message ) ;
92+ }
93+ }
94+
95+ private uploadToArtifacts ( pathToFile : string ) {
96+ const artifactName = 'scanoss' ;
97+ tl . command ( 'artifact.upload' , { artifactname : artifactName } , pathToFile ) ;
98+ }
99+ }
100+
101+ export const scanossService = new ScanOssService ( ) ;
0 commit comments