• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Copyright (c) 2025 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import argparse
17import os
18import shutil
19import subprocess
20import sys
21import tarfile
22import time
23
24
25def copy_files(source_path, dest_path, is_file=False):
26    try:
27        if is_file:
28            if not os.path.exists(os.path.dirname(dest_path)):
29                os.makedirs(os.path.dirname(dest_path), exist_ok=True)
30            shutil.copy(source_path, dest_path)
31        else:
32            shutil.copytree(source_path, dest_path, dirs_exist_ok=True,
33                symlinks=True)
34    except Exception as err:
35        raise Exception("Copy files failed. Error: " + str(err)) from err
36
37
38def run_cmd(cmd, execution_path=None):
39    if (cmd and cmd[0].strip().endswith('npm')):
40        cmd.append('--registry')
41        cmd.append('https://repo.huaweicloud.com/repository/npm/')
42    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
43                           stdin=subprocess.PIPE,
44                           stderr=subprocess.PIPE,
45                           cwd=execution_path)
46    stdout, stderr = proc.communicate(timeout=600)
47    if proc.returncode != 0:
48        raise Exception(stderr.decode())
49    return stdout
50
51
52def run_cmd_with_retry(max_retries, wait_time, cmd, execution_path=None):
53    retry_count = 0
54    last_exception = None
55    while retry_count < max_retries:
56        try:
57            run_cmd(cmd, execution_path)
58            break
59        except Exception as e:
60            last_exception = e
61            retry_count += 1
62            time.sleep(wait_time)
63    if retry_count >= max_retries:
64        raise Exception(
65            "Command failed after {r} attempts. Cmd: {c}. Error: {e}"
66            .format(
67                r=max_retries,
68                c=" ".join(cmd),
69                e=last_exception
70            )
71        )
72
73
74def is_npm_newer_than_6(options):
75    cmd = [options.npm, '-v']
76    stdout = run_cmd(cmd, options.source_path)
77    version_str = stdout.decode('utf-8').strip()
78    # get npm major version(i.e. "6.14.15" -> 6)
79    major_version = int(version_str.split('.')[0])
80    if major_version is not None:
81        if major_version <= 6:
82            return False
83        else:
84            return True
85    # default set to lower than v7 which can compatible with v7+
86    return False
87
88
89def build(options):
90    build_cmd = [options.npm, 'run', 'build']
91    pack_cmd = [options.npm, 'pack']
92    run_cmd(build_cmd, options.source_path)
93    run_cmd(pack_cmd, options.source_path)
94
95
96def copy_output(options):
97    run_cmd(['rm', '-rf', options.output_path])
98    src = os.path.join(options.source_path, 'panda-tslinter-{}.tgz'.format(options.version))
99    dest = os.path.join(options.output_path, 'panda-tslinter-{}.tgz'.format(options.version))
100    copy_files(src, dest, True)
101    try:
102        with tarfile.open(dest, 'r:gz') as tar:
103            tar.extractall(path=options.output_path)
104    except tarfile.TarError as e:
105        raise Exception("Error extracting files") from e
106    copy_files(os.path.join(options.output_path, 'package'), options.output_path)
107    run_cmd(['rm', '-rf', os.path.join(options.output_path, 'package')])
108    run_cmd(['rm', '-rf', dest])
109    src = os.path.join(options.source_path, 'tsconfig.json')
110    dest = os.path.join(options.output_path, 'tsconfig.json')
111    copy_files(src, dest, True)
112
113
114def install_typescript(options):
115    new_npm = is_npm_newer_than_6(options)
116    tsc_file = 'file:' + options.typescript
117    if new_npm:
118        cmd = [options.npm, 'install', '--no-save', tsc_file, '--legacy-peer-deps', '--offline']
119    else:
120        cmd = [options.npm, 'install', '--no-save', tsc_file]
121    run_cmd(cmd, options.source_path)
122
123
124def find_files_by_prefix_suffix(directory, prefix, suffix):
125    matched_files = []
126    for filename in os.listdir(directory):
127        if filename.startswith(prefix) and filename.endswith(suffix):
128            matched_files.append(os.path.join(directory, filename))
129    return sorted(matched_files, key=os.path.getctime, reverse=True)
130
131
132def clean_old_packages(directory, prefix, suffix):
133    res = True
134    matched_files = find_files_by_prefix_suffix(directory, prefix, suffix)
135    if (matched_files):
136        for file in matched_files:
137            try:
138                os.remove(file)
139            except Exception:
140                res = False
141    return res
142
143
144def backup_package_files(source_path):
145    package_name = 'package.json'
146    package_back_name = 'package.json.bak'
147    aa_path = os.path.join(source_path, 'arkanalyzer')
148    hc_path = os.path.join(source_path, 'homecheck')
149    linter_path = source_path
150    copy_files(os.path.join(aa_path, package_name), os.path.join(aa_path, package_back_name), True)
151    copy_files(os.path.join(hc_path, package_name), os.path.join(hc_path, package_back_name), True)
152    copy_files(os.path.join(linter_path, package_name), os.path.join(linter_path, package_back_name), True)
153
154
155def clean_env(source_path):
156    package_name = 'package.json'
157    package_back_name = 'package.json.bak'
158    package_lock_name = 'package-lock.json'
159    aa_path = os.path.join(source_path, 'arkanalyzer')
160    hc_path = os.path.join(source_path, 'homecheck')
161    linter_path = source_path
162    try:
163        copy_files(os.path.join(aa_path, package_back_name), os.path.join(aa_path, package_name), True)
164        copy_files(os.path.join(hc_path, package_back_name), os.path.join(hc_path, package_name), True)
165        copy_files(os.path.join(linter_path, package_back_name), os.path.join(linter_path, package_name), True)
166        os.remove(os.path.join(hc_path, package_lock_name))
167        os.remove(os.path.join(linter_path, package_lock_name))
168        os.remove(os.path.join(aa_path, package_back_name))
169        os.remove(os.path.join(hc_path, package_back_name))
170        os.remove(os.path.join(linter_path, package_back_name))
171    except Exception:
172        return False
173    return True
174
175
176def aa_copy_lib_files(options):
177    aa_path = os.path.join(options.source_path, 'arkanalyzer')
178    source_file_1 = os.path.join(aa_path, 'node_modules', 'ohos-typescript', 'lib', 'lib.es5.d.ts')
179    dest_path = os.path.join(aa_path, 'builtIn', 'typescript', 'api', '@internal')
180    copy_files(source_file_1, dest_path, True)
181    source_file_2 = os.path.join(aa_path, 'node_modules', 'ohos-typescript', 'lib', 'lib.es2015.collection.d.ts')
182    copy_files(source_file_2, dest_path, True)
183
184
185def hc_copy_lib_files(options):
186    hc_path = os.path.join(options.source_path, 'homecheck')
187    source_file = os.path.join(hc_path, 'node_modules', 'ohos-typescript', 'lib', 'lib.es5.d.ts')
188    dest_path = os.path.join(hc_path, 'resources', 'internalSdk', '@internal')
189    copy_files(source_file, dest_path, True)
190
191
192def pack_arkanalyzer(options, new_npm):
193    aa_path = os.path.join(options.source_path, 'arkanalyzer')
194    tsc_file = 'file:' + options.typescript
195    pack_prefix = 'arkanalyzer-'
196    pack_suffix = '.tgz'
197    clean_old_packages(aa_path, pack_prefix, pack_suffix)
198
199    if new_npm:
200        ts_install_cmd = [options.npm, 'install', '--no-save', tsc_file, '--legacy-peer-deps', '--offline']
201    else:
202        ts_install_cmd = [options.npm, 'install', '--no-save', tsc_file]
203    compile_cmd = [options.npm, 'run', 'compile']
204    pack_cmd = [options.npm, 'pack']
205    run_cmd(ts_install_cmd, aa_path)
206    aa_copy_lib_files(options)
207    run_cmd(compile_cmd, aa_path)
208    run_cmd(pack_cmd, aa_path)
209
210
211def install_homecheck(options, max_retries, wait_time):
212    new_npm = is_npm_newer_than_6(options)
213    pack_arkanalyzer(options, new_npm)
214    aa_path = os.path.join(options.source_path, 'arkanalyzer')
215    hc_path = os.path.join(options.source_path, 'homecheck')
216    aa_pack_prefix = 'arkanalyzer-'
217    hc_pack_prefix = 'homecheck-'
218    pack_suffix = '.tgz'
219    exist_aa_packs = find_files_by_prefix_suffix(aa_path, aa_pack_prefix, pack_suffix)
220    if (exist_aa_packs):
221        aa_file = 'file:' + exist_aa_packs[0]
222        if new_npm:
223            aa_install_cmd = [options.npm, 'install', aa_file, '--legacy-peer-deps', '--offline']
224        else:
225            aa_install_cmd = [options.npm, 'install', aa_file]
226        run_cmd_with_retry(max_retries, wait_time, aa_install_cmd, hc_path)
227    else:
228        raise Exception('Failed to find arkanalyzer npm package')
229
230    clean_old_packages(hc_path, hc_pack_prefix, pack_suffix)
231    tsc_file = 'file:' + options.typescript
232    if new_npm:
233        ts_install_cmd = [options.npm, 'install', '--no-save', tsc_file, '--legacy-peer-deps', '--offline']
234    else:
235        ts_install_cmd = [options.npm, 'install', '--no-save', tsc_file]
236    pack_cmd = [options.npm, 'pack']
237    compile_cmd = [options.npm, 'run', 'compile']
238    run_cmd_with_retry(max_retries, wait_time, ts_install_cmd, hc_path)
239    hc_copy_lib_files(options)
240    run_cmd(compile_cmd, hc_path)
241    run_cmd(pack_cmd, hc_path)
242    exist_hc_packs = find_files_by_prefix_suffix(hc_path, hc_pack_prefix, pack_suffix)
243    if (exist_hc_packs):
244        hc_file = 'file:' + exist_hc_packs[0]
245        if new_npm:
246            hc_install_cmd = [options.npm, 'install', hc_file, '--legacy-peer-deps', '--offline']
247        else:
248            hc_install_cmd = [options.npm, 'install', hc_file]
249        run_cmd_with_retry(max_retries, wait_time, hc_install_cmd, options.source_path)
250    else:
251        raise Exception('Failed to find homecheck npm package')
252
253
254def extract(package_path, dest_path, package_name):
255    try:
256        with tarfile.open(package_path, 'r:gz') as tar:
257            tar.extractall(path=dest_path)
258    except tarfile.TarError as e:
259        raise Exception("Error extracting files") from e
260    dest_package_path = os.path.join(dest_path, package_name)
261    if (os.path.exists(dest_package_path)):
262        shutil.rmtree(dest_package_path)
263    os.rename(os.path.join(dest_path, 'package'), dest_package_path)
264
265
266def parse_args():
267    parser = argparse.ArgumentParser()
268    parser.add_argument('--npm', help='path to a npm exetuable')
269    parser.add_argument('--source-path', help='path to build system source')
270    parser.add_argument('--output-path', help='path to output')
271    parser.add_argument('--typescript', help='path to typescript')
272    parser.add_argument('--version', help='linter version')
273
274    options = parser.parse_args()
275    return options
276
277
278def main():
279    options = parse_args()
280    backup_package_files(options.source_path)
281    install_homecheck(options, 5, 3)
282    install_typescript(options)
283    node_modules_path = os.path.join(options.source_path, "node_modules")
284    extract(options.typescript, node_modules_path, "typescript")
285    build(options)
286    copy_output(options)
287    clean_env(options.source_path)
288
289
290if __name__ == '__main__':
291    sys.exit(main())
292