@capgo/cli 4.10.2 → 4.10.6-beta.1

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.
@@ -15,6 +15,7 @@ export interface Database {
15
15
  id: number
16
16
  key: string
17
17
  mode: Database['public']['Enums']['key_mode']
18
+ name: string
18
19
  updated_at: string | null
19
20
  user_id: string
20
21
  }
@@ -23,6 +24,7 @@ export interface Database {
23
24
  id?: number
24
25
  key: string
25
26
  mode: Database['public']['Enums']['key_mode']
27
+ name: string
26
28
  updated_at?: string | null
27
29
  user_id: string
28
30
  }
@@ -31,6 +33,7 @@ export interface Database {
31
33
  id?: number
32
34
  key?: string
33
35
  mode?: Database['public']['Enums']['key_mode']
36
+ name?: string
34
37
  updated_at?: string | null
35
38
  user_id?: string
36
39
  }
@@ -411,129 +414,6 @@ export interface Database {
411
414
  },
412
415
  ]
413
416
  }
414
- clickhouse_app_usage_parm: {
415
- Row: {
416
- _app_list: string | null
417
- _end_date: string | null
418
- _start_date: string | null
419
- app_id: string | null
420
- bandwidth: number | null
421
- date: string | null
422
- fail: number | null
423
- get: number | null
424
- install: number | null
425
- mau: number | null
426
- storage_added: number | null
427
- storage_deleted: number | null
428
- uninstall: number | null
429
- }
430
- Insert: {
431
- _app_list?: string | null
432
- _end_date?: string | null
433
- _start_date?: string | null
434
- app_id?: string | null
435
- bandwidth?: number | null
436
- date?: string | null
437
- fail?: number | null
438
- get?: number | null
439
- install?: number | null
440
- mau?: number | null
441
- storage_added?: number | null
442
- storage_deleted?: number | null
443
- uninstall?: number | null
444
- }
445
- Update: {
446
- _app_list?: string | null
447
- _end_date?: string | null
448
- _start_date?: string | null
449
- app_id?: string | null
450
- bandwidth?: number | null
451
- date?: string | null
452
- fail?: number | null
453
- get?: number | null
454
- install?: number | null
455
- mau?: number | null
456
- storage_added?: number | null
457
- storage_deleted?: number | null
458
- uninstall?: number | null
459
- }
460
- Relationships: []
461
- }
462
- clickhouse_devices: {
463
- Row: {
464
- app_id: string | null
465
- created_at: string | null
466
- custom_id: string | null
467
- device_id: string | null
468
- is_emulator: boolean | null
469
- is_prod: boolean | null
470
- os_version: string | null
471
- platform: string | null
472
- plugin_version: string | null
473
- updated_at: string | null
474
- version: number | null
475
- version_build: string | null
476
- }
477
- Insert: {
478
- app_id?: string | null
479
- created_at?: string | null
480
- custom_id?: string | null
481
- device_id?: string | null
482
- is_emulator?: boolean | null
483
- is_prod?: boolean | null
484
- os_version?: string | null
485
- platform?: string | null
486
- plugin_version?: string | null
487
- updated_at?: string | null
488
- version?: number | null
489
- version_build?: string | null
490
- }
491
- Update: {
492
- app_id?: string | null
493
- created_at?: string | null
494
- custom_id?: string | null
495
- device_id?: string | null
496
- is_emulator?: boolean | null
497
- is_prod?: boolean | null
498
- os_version?: string | null
499
- platform?: string | null
500
- plugin_version?: string | null
501
- updated_at?: string | null
502
- version?: number | null
503
- version_build?: string | null
504
- }
505
- Relationships: []
506
- }
507
- clickhouse_logs: {
508
- Row: {
509
- action: string | null
510
- app_id: string | null
511
- created_at: string | null
512
- device_id: string | null
513
- platform: string | null
514
- version: number | null
515
- version_build: string | null
516
- }
517
- Insert: {
518
- action?: string | null
519
- app_id?: string | null
520
- created_at?: string | null
521
- device_id?: string | null
522
- platform?: string | null
523
- version?: number | null
524
- version_build?: string | null
525
- }
526
- Update: {
527
- action?: string | null
528
- app_id?: string | null
529
- created_at?: string | null
530
- device_id?: string | null
531
- platform?: string | null
532
- version?: number | null
533
- version_build?: string | null
534
- }
535
- Relationships: []
536
- }
537
417
  daily_bandwidth: {
538
418
  Row: {
539
419
  app_id: string
@@ -669,13 +549,12 @@ export interface Database {
669
549
  devices: {
670
550
  Row: {
671
551
  app_id: string
672
- created_at: string
673
552
  custom_id: string
674
553
  device_id: string
675
554
  is_emulator: boolean | null
676
555
  is_prod: boolean | null
677
556
  os_version: string | null
678
- platform: Database['public']['Enums']['platform_os'] | null
557
+ platform: Database['public']['Enums']['platform_os']
679
558
  plugin_version: string
680
559
  updated_at: string
681
560
  version: number
@@ -683,13 +562,12 @@ export interface Database {
683
562
  }
684
563
  Insert: {
685
564
  app_id: string
686
- created_at: string
687
565
  custom_id?: string
688
566
  device_id: string
689
567
  is_emulator?: boolean | null
690
568
  is_prod?: boolean | null
691
569
  os_version?: string | null
692
- platform?: Database['public']['Enums']['platform_os'] | null
570
+ platform: Database['public']['Enums']['platform_os']
693
571
  plugin_version?: string
694
572
  updated_at: string
695
573
  version: number
@@ -697,13 +575,12 @@ export interface Database {
697
575
  }
698
576
  Update: {
699
577
  app_id?: string
700
- created_at?: string
701
578
  custom_id?: string
702
579
  device_id?: string
703
580
  is_emulator?: boolean | null
704
581
  is_prod?: boolean | null
705
582
  os_version?: string | null
706
- platform?: Database['public']['Enums']['platform_os'] | null
583
+ platform?: Database['public']['Enums']['platform_os']
707
584
  plugin_version?: string
708
585
  updated_at?: string
709
586
  version?: number
@@ -1072,31 +949,25 @@ export interface Database {
1072
949
  }
1073
950
  stats: {
1074
951
  Row: {
1075
- action: string
952
+ action: Database['public']['Enums']['stats_action']
1076
953
  app_id: string
1077
954
  created_at: string
1078
955
  device_id: string
1079
- platform: Database['public']['Enums']['platform_os']
1080
956
  version: number
1081
- version_build: string
1082
957
  }
1083
958
  Insert: {
1084
- action: string
959
+ action: Database['public']['Enums']['stats_action']
1085
960
  app_id: string
1086
961
  created_at: string
1087
962
  device_id: string
1088
- platform: Database['public']['Enums']['platform_os']
1089
963
  version: number
1090
- version_build: string
1091
964
  }
1092
965
  Update: {
1093
- action?: string
966
+ action?: Database['public']['Enums']['stats_action']
1094
967
  app_id?: string
1095
968
  created_at?: string
1096
969
  device_id?: string
1097
- platform?: Database['public']['Enums']['platform_os']
1098
970
  version?: number
1099
- version_build?: string
1100
971
  }
1101
972
  Relationships: []
1102
973
  }
@@ -1266,19 +1137,19 @@ export interface Database {
1266
1137
  }
1267
1138
  version_usage: {
1268
1139
  Row: {
1269
- action: string
1140
+ action: Database['public']['Enums']['version_action']
1270
1141
  app_id: string
1271
1142
  timestamp: string
1272
1143
  version_id: number
1273
1144
  }
1274
1145
  Insert: {
1275
- action: string
1146
+ action: Database['public']['Enums']['version_action']
1276
1147
  app_id: string
1277
1148
  timestamp?: string
1278
1149
  version_id: number
1279
1150
  }
1280
1151
  Update: {
1281
- action?: string
1152
+ action?: Database['public']['Enums']['version_action']
1282
1153
  app_id?: string
1283
1154
  timestamp?: string
1284
1155
  version_id?: number
@@ -1608,6 +1479,13 @@ export interface Database {
1608
1479
  role: Database['public']['Enums']['user_min_right']
1609
1480
  }[]
1610
1481
  }
1482
+ get_org_owner_id: {
1483
+ Args: {
1484
+ apikey: string
1485
+ app_id: string
1486
+ }
1487
+ Returns: string
1488
+ }
1611
1489
  get_org_perm_for_apikey: {
1612
1490
  Args: {
1613
1491
  apikey: string
@@ -1717,16 +1595,6 @@ export interface Database {
1717
1595
  uninstall: number
1718
1596
  }[]
1719
1597
  }
1720
- get_total_stats_v5_org: {
1721
- Args: {
1722
- orgid: string
1723
- }
1724
- Returns: {
1725
- mau: number
1726
- bandwidth: number
1727
- storage: number
1728
- }[]
1729
- }
1730
1598
  get_total_storage_size:
1731
1599
  | {
1732
1600
  Args: {
@@ -1773,6 +1641,27 @@ export interface Database {
1773
1641
  }
1774
1642
  Returns: string
1775
1643
  }
1644
+ get_versions_with_no_metadata: {
1645
+ Args: Record<PropertyKey, never>
1646
+ Returns: {
1647
+ app_id: string
1648
+ bucket_id: string | null
1649
+ checksum: string | null
1650
+ created_at: string | null
1651
+ deleted: boolean
1652
+ external_url: string | null
1653
+ id: number
1654
+ minUpdateVersion: string | null
1655
+ name: string
1656
+ native_packages: Json[] | null
1657
+ owner_org: string
1658
+ r2_path: string | null
1659
+ session_key: string | null
1660
+ storage_provider: string
1661
+ updated_at: string | null
1662
+ user_id: string | null
1663
+ }[]
1664
+ }
1776
1665
  get_weekly_stats: {
1777
1666
  Args: {
1778
1667
  app_id: string
@@ -1798,6 +1687,14 @@ export interface Database {
1798
1687
  }
1799
1688
  Returns: boolean
1800
1689
  }
1690
+ http_post_helper: {
1691
+ Args: {
1692
+ function_name: string
1693
+ function_type: string
1694
+ body: Json
1695
+ }
1696
+ Returns: number
1697
+ }
1801
1698
  invite_user_to_org: {
1802
1699
  Args: {
1803
1700
  email: string
@@ -1846,27 +1743,6 @@ export interface Database {
1846
1743
  }
1847
1744
  Returns: boolean
1848
1745
  }
1849
- | {
1850
- Args: {
1851
- apikey: string
1852
- keymode: Database['public']['Enums']['key_mode'][]
1853
- app_id: string
1854
- channel_id: number
1855
- right: Database['public']['Enums']['user_min_right']
1856
- user_id: string
1857
- }
1858
- Returns: boolean
1859
- }
1860
- | {
1861
- Args: {
1862
- apikey: string
1863
- keymode: Database['public']['Enums']['key_mode'][]
1864
- app_id: string
1865
- right: Database['public']['Enums']['user_min_right']
1866
- user_id: string
1867
- }
1868
- Returns: boolean
1869
- }
1870
1746
  is_app_owner:
1871
1747
  | {
1872
1748
  Args: {
@@ -1962,6 +1838,10 @@ export interface Database {
1962
1838
  Args: Record<PropertyKey, never>
1963
1839
  Returns: number[]
1964
1840
  }
1841
+ process_failed_uploads: {
1842
+ Args: Record<PropertyKey, never>
1843
+ Returns: undefined
1844
+ }
1965
1845
  process_free_trial_expired: {
1966
1846
  Args: Record<PropertyKey, never>
1967
1847
  Returns: undefined
@@ -2061,6 +1941,50 @@ export interface Database {
2061
1941
  pay_as_you_go_type: 'base' | 'units'
2062
1942
  platform_os: 'ios' | 'android'
2063
1943
  queue_job_status: 'inserted' | 'requested' | 'failed'
1944
+ stats_action:
1945
+ | 'delete'
1946
+ | 'reset'
1947
+ | 'set'
1948
+ | 'get'
1949
+ | 'set_fail'
1950
+ | 'update_fail'
1951
+ | 'download_fail'
1952
+ | 'windows_path_fail'
1953
+ | 'canonical_path_fail'
1954
+ | 'directory_path_fail'
1955
+ | 'unzip_fail'
1956
+ | 'low_mem_fail'
1957
+ | 'download_10'
1958
+ | 'download_20'
1959
+ | 'download_30'
1960
+ | 'download_40'
1961
+ | 'download_50'
1962
+ | 'download_60'
1963
+ | 'download_70'
1964
+ | 'download_80'
1965
+ | 'download_90'
1966
+ | 'download_complete'
1967
+ | 'decrypt_fail'
1968
+ | 'app_moved_to_foreground'
1969
+ | 'app_moved_to_background'
1970
+ | 'uninstall'
1971
+ | 'needPlanUpgrade'
1972
+ | 'missingBundle'
1973
+ | 'noNew'
1974
+ | 'disablePlatformIos'
1975
+ | 'disablePlatformAndroid'
1976
+ | 'disableAutoUpdateToMajor'
1977
+ | 'cannotUpdateViaPrivateChannel'
1978
+ | 'disableAutoUpdateToMinor'
1979
+ | 'disableAutoUpdateToPatch'
1980
+ | 'channelMisconfigured'
1981
+ | 'disableAutoUpdateMetadata'
1982
+ | 'disableAutoUpdateUnderNative'
1983
+ | 'disableDevBuild'
1984
+ | 'disableEmulator'
1985
+ | 'cannotGetBundle'
1986
+ | 'checksum_fail'
1987
+ | 'NoChannelOrOverride'
2064
1988
  stripe_status:
2065
1989
  | 'created'
2066
1990
  | 'succeeded'
@@ -2081,6 +2005,7 @@ export interface Database {
2081
2005
  | 'admin'
2082
2006
  | 'super_admin'
2083
2007
  user_role: 'read' | 'upload' | 'write' | 'admin'
2008
+ version_action: 'get' | 'fail' | 'install' | 'uninstall'
2084
2009
  }
2085
2010
  CompositeTypes: {
2086
2011
  match_plan: {
package/src/utils.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { existsSync, readFileSync, readdirSync } from 'node:fs'
1
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'
2
2
  import { homedir } from 'node:os'
3
- import { resolve } from 'node:path'
3
+ import { join, resolve, sep } from 'node:path'
4
4
  import process from 'node:process'
5
5
  import type { Buffer } from 'node:buffer'
6
6
  import { loadConfig } from '@capacitor/cli/dist/config'
@@ -15,6 +15,9 @@ import { promiseFiles } from 'node-dir'
15
15
  import { findRootSync } from '@manypkg/find-root'
16
16
  import type { InstallCommand, PackageManagerRunner, PackageManagerType } from '@capgo/find-package-manager'
17
17
  import { findInstallCommand, findPackageManagerRunner, findPackageManagerType } from '@capgo/find-package-manager'
18
+ // import AdmZip from 'adm-zip'
19
+ // import isWsl from 'is-wsl'
20
+ import JSZip from 'jszip'
18
21
  import type { Database } from './types/supabase.types'
19
22
 
20
23
  export const baseKey = '.capgo_key'
@@ -27,7 +30,7 @@ export type ArrayElement<ArrayType extends readonly unknown[]> =
27
30
  ArrayType extends readonly (infer ElementType)[] ? ElementType : never
28
31
  export type Organization = ArrayElement<Database['public']['Functions']['get_orgs_v5']['Returns']>
29
32
 
30
- export const regexSemver = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
33
+ export const regexSemver = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-z-][0-9a-z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-z-][0-9a-z-]*))*))?(?:\+([0-9a-z-]+(?:\.[0-9a-z-]+)*))?$/i
31
34
  export const formatError = (error: any) => error ? `\n${prettyjson.render(error)}` : ''
32
35
 
33
36
  export interface OptionsBase {
@@ -74,7 +77,7 @@ export async function getLocalConfig() {
74
77
  }
75
78
  }
76
79
  }
77
-
80
+ // eslint-disable-next-line regexp/no-unused-capturing-group
78
81
  const nativeFileRegex = /([A-Za-z0-9]+)\.(java|swift|kt|scala)$/
79
82
 
80
83
  interface CapgoConfig {
@@ -134,13 +137,6 @@ export async function checkKey(supabase: SupabaseClient<Database>, apikey: strin
134
137
  }
135
138
  }
136
139
 
137
- export async function isGoodPlan(supabase: SupabaseClient<Database>, userId: string): Promise<boolean> {
138
- const { data } = await supabase
139
- .rpc('is_good_plan_v5', { userid: userId })
140
- .single()
141
- return data || false
142
- }
143
-
144
140
  export async function isPayingOrg(supabase: SupabaseClient<Database>, orgId: string): Promise<boolean> {
145
141
  const { data } = await supabase
146
142
  .rpc('is_paying_org', { orgid: orgId })
@@ -326,10 +322,13 @@ async function* getFiles(dir: string): AsyncGenerator<string> {
326
322
  if (dirent.isDirectory()
327
323
  && !dirent.name.startsWith('.')
328
324
  && !dirent.name.startsWith('node_modules')
329
- && !dirent.name.startsWith('dist'))
325
+ && !dirent.name.startsWith('dist')) {
330
326
  yield * getFiles(res)
331
- else
327
+ }
328
+
329
+ else {
332
330
  yield res
331
+ }
333
332
  }
334
333
  }
335
334
 
@@ -418,6 +417,7 @@ export async function findBuildCommandForProjectType(projectType: string) {
418
417
  }
419
418
 
420
419
  export async function findMainFile() {
420
+ // eslint-disable-next-line regexp/no-unused-capturing-group
421
421
  const mainRegex = /(main|index)\.(ts|tsx|js|jsx)$/
422
422
  // search for main.ts or main.js in local dir and subdirs
423
423
  let mainFile = ''
@@ -519,6 +519,46 @@ async function prepareMultipart(supabase: SupabaseClient<Database>, appId: strin
519
519
  }
520
520
  }
521
521
 
522
+ export async function zipFile(filePath: string): Promise<Buffer> {
523
+ const zip = new JSZip()
524
+
525
+ // Helper function to recursively add files and folders to the ZIP archive
526
+ const addToZip = async (folderPath: string, zipPath: string) => {
527
+ const items = readdirSync(folderPath)
528
+
529
+ for (const item of items) {
530
+ const itemPath = join(folderPath, item)
531
+ const stats = statSync(itemPath)
532
+
533
+ if (stats.isFile()) {
534
+ const fileContent = await readFileSync(itemPath)
535
+ zip.file(join(zipPath, item).split(sep).join('/'), fileContent)
536
+ }
537
+ else if (stats.isDirectory()) {
538
+ await addToZip(itemPath, join(zipPath, item))
539
+ }
540
+ }
541
+ }
542
+
543
+ // Start adding files and folders to the ZIP archive
544
+ await addToZip(filePath, '')
545
+
546
+ // Generate the ZIP file as a Buffer
547
+ const zipBuffer = await zip.generateAsync({ type: 'nodebuffer', platform: 'UNIX' })
548
+ return zipBuffer
549
+ }
550
+
551
+ // export function zipFile(filePath: string) {
552
+ // // if windows and not wsl then do error
553
+ // if (os.release().toLowerCase().includes('microsoft') && !isWsl) {
554
+ // p.log.error(`Windows powershell is not supported, please use WSL or a Linux distribution`)
555
+ // program.error('')
556
+ // }
557
+ // const zip = new AdmZip()
558
+ // zip.addLocalFolder(filePath)
559
+ // return zip.toBuffer()
560
+ // }
561
+
522
562
  async function finishMultipartDownload(key: string, uploadId: string, url: string, parts: any[]) {
523
563
  const metadata = {
524
564
  action: 'mpu-complete',
@@ -537,8 +577,19 @@ async function finishMultipartDownload(key: string, uploadId: string, url: strin
537
577
  }
538
578
 
539
579
  const PART_SIZE = 10 * 1024 * 1024
540
- export async function uploadMultipart(supabase: SupabaseClient<Database>, appId: string, name: string, data: Buffer): Promise<boolean> {
580
+ export async function uploadMultipart(supabase: SupabaseClient<Database>, appId: string, name: string, data: Buffer, orgId: string): Promise<boolean> {
541
581
  try {
582
+ const snag = useLogSnag()
583
+ await snag.track({
584
+ channel: 'app',
585
+ event: 'App Multipart Prepare',
586
+ icon: '⏫',
587
+ user_id: orgId,
588
+ tags: {
589
+ 'app-id': appId,
590
+ },
591
+ notify: false,
592
+ }).catch()
542
593
  const multipartPrep = await prepareMultipart(supabase, appId, name)
543
594
  if (!multipartPrep) {
544
595
  // Just pass the error
@@ -555,6 +606,16 @@ export async function uploadMultipart(supabase: SupabaseClient<Database>, appId:
555
606
 
556
607
  await finishMultipartDownload(multipartPrep.key, multipartPrep.uploadId, multipartPrep.url, parts)
557
608
 
609
+ await snag.track({
610
+ channel: 'app',
611
+ event: 'App Multipart done',
612
+ icon: '⏫',
613
+ user_id: orgId,
614
+ tags: {
615
+ 'app-id': appId,
616
+ },
617
+ notify: false,
618
+ }).catch()
558
619
  return true
559
620
  }
560
621
  catch (e) {
@@ -571,7 +632,7 @@ export async function deletedFailedVersion(supabase: SupabaseClient<Database>, a
571
632
  try {
572
633
  const pathFailed = 'private/delete_failed_version'
573
634
  const res = await supabase.functions.invoke(pathFailed, { body: JSON.stringify(data), method: 'DELETE' })
574
- return res.data.url
635
+ return res.data.status
575
636
  }
576
637
  catch (error) {
577
638
  p.log.error(`Cannot delete failed version ${formatError(error)}`)
@@ -0,0 +1,83 @@
1
+ import java.io.File;
2
+ import java.io.FileInputStream;
3
+ import java.io.IOException;
4
+ import java.io.BufferedInputStream;
5
+ import java.io.FileNotFoundException;
6
+ import java.io.FileOutputStream;
7
+ import java.util.zip.ZipEntry;
8
+ import java.util.zip.ZipInputStream;
9
+
10
+ public class VerifyZip {
11
+ public static void main(String[] args) {
12
+ if (args.length < 1) {
13
+ System.out.println("Usage: java VerifyZip <zip-file>");
14
+ System.exit(1);
15
+ }
16
+
17
+ String zipFilePath = args[0];
18
+ File zipFile = new File(zipFilePath);
19
+ File targetDirectory = new File("extracted");
20
+
21
+ if (!zipFile.exists()) {
22
+ System.out.println("File not found: " + zipFilePath);
23
+ System.exit(1);
24
+ }
25
+
26
+ try (
27
+ BufferedInputStream bis = new BufferedInputStream(new FileInputStream(zipFile));
28
+ ZipInputStream zis = new ZipInputStream(bis)
29
+ ) {
30
+ int count;
31
+ int bufferSize = 8192;
32
+ byte[] buffer = new byte[bufferSize];
33
+ long lengthTotal = zipFile.length();
34
+ long lengthRead = bufferSize;
35
+ int percent = 0;
36
+
37
+ ZipEntry entry;
38
+ while ((entry = zis.getNextEntry()) != null) {
39
+ if (entry.getName().contains("\\")) {
40
+ System.out.println("Windows path is not supported: " + entry.getName());
41
+ System.exit(1);
42
+ }
43
+ File file = new File(targetDirectory, entry.getName());
44
+ String canonicalPath = file.getCanonicalPath();
45
+ String canonicalDir = targetDirectory.getCanonicalPath();
46
+ File dir = entry.isDirectory() ? file : file.getParentFile();
47
+
48
+ if (!canonicalPath.startsWith(canonicalDir)) {
49
+ System.out.println("SecurityException, Failed to ensure directory is the start path: " +
50
+ canonicalDir + " of " + canonicalPath);
51
+ System.exit(1);
52
+ }
53
+
54
+ if (!dir.isDirectory() && !dir.mkdirs()) {
55
+ System.out.println("Failed to ensure directory: " + dir.getAbsolutePath());
56
+ System.exit(1);
57
+ }
58
+
59
+ if (entry.isDirectory()) {
60
+ continue;
61
+ }
62
+
63
+ try (FileOutputStream outputStream = new FileOutputStream(file)) {
64
+ while ((count = zis.read(buffer)) != -1) {
65
+ outputStream.write(buffer, 0, count);
66
+ }
67
+ }
68
+
69
+ int newPercent = (int) ((lengthRead / (float) lengthTotal) * 100);
70
+ if (lengthTotal > 1 && newPercent != percent) {
71
+ percent = newPercent;
72
+ }
73
+
74
+ lengthRead += entry.getCompressedSize();
75
+ }
76
+ System.out.println("ZIP file is valid: " + zipFilePath);
77
+ } catch (IOException e) {
78
+ System.out.println("Failed to process ZIP file: " + zipFilePath);
79
+ e.printStackTrace();
80
+ System.exit(1);
81
+ }
82
+ }
83
+ }