Merge remote-tracking branch 'origin/dev' into release-candidate
This commit is contained in:
		
						commit
						e2e4dcc2f8
					
				
							
								
								
									
										11
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -96,14 +96,14 @@ jobs: | ||||
| 
 | ||||
|       - name: 'Upload map analyser files to storage' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
|         uses: keithweaver/aws-s3-github-action@v1.0.0 | ||||
|         uses: prewk/s3-cp-action@v2 | ||||
|         with: | ||||
|           source: map_analyser_files/ | ||||
|           destination: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" | ||||
|           aws_s3_endpoint: "${{ secrets.MAP_REPORT_AWS_ENDPOINT }}" | ||||
|           aws_access_key_id: "${{ secrets.MAP_REPORT_AWS_ACCESS_KEY }}" | ||||
|           aws_secret_access_key: "${{ secrets.MAP_REPORT_AWS_SECRET_KEY }}" | ||||
|           aws_region: "${{ secrets.MAP_REPORT_AWS_REGION }}" | ||||
|           flags: --recursive | ||||
|           source: "./map_analyser_files/" | ||||
|           dest: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" | ||||
|           flags: "--recursive --acl public-read" | ||||
| 
 | ||||
|       - name: 'Trigger map file reporter' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
| @ -114,7 +114,6 @@ jobs: | ||||
|           event-type: map-file-analyse | ||||
|           client-payload: '{"random_hash": "${{steps.names.outputs.random_hash}}", "event_type": "${{steps.names.outputs.event_type}}"}' | ||||
| 
 | ||||
| 
 | ||||
|       - name: 'Upload artifacts to update server' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
|         run: | | ||||
|  | ||||
							
								
								
									
										21
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							| @ -54,17 +54,16 @@ jobs: | ||||
|           ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1 | ||||
|           echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT | ||||
| 
 | ||||
|       - name: 'Upload artifacts to update server' | ||||
|       - name: 'Upload report' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }} | ||||
|         run: | | ||||
|           mkdir -p ~/.ssh | ||||
|           ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts | ||||
|           echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; | ||||
|           chmod 600 ./deploy_key; | ||||
|           rsync -avrzP --mkpath \ | ||||
|               -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ | ||||
|               build/f7-firmware-DC/pvsreport/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:/home/data/firmware-pvs-studio-report/"${BRANCH_NAME}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/"; | ||||
|           rm ./deploy_key; | ||||
|         uses: prewk/s3-cp-action@v2 | ||||
|         with: | ||||
|           aws_s3_endpoint: "${{ secrets.PVS_AWS_ENDPOINT }}" | ||||
|           aws_access_key_id: "${{ secrets.PVS_AWS_ACCESS_KEY }}" | ||||
|           aws_secret_access_key: "${{ secrets.PVS_AWS_SECRET_KEY }}" | ||||
|           source: "./build/f7-firmware-DC/pvsreport" | ||||
|           dest: "s3://${{ secrets.PVS_AWS_BUCKET }}/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/" | ||||
|           flags: "--recursive --acl public-read" | ||||
| 
 | ||||
|       - name: 'Find Previous Comment' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} | ||||
| @ -83,7 +82,7 @@ jobs: | ||||
|           issue-number: ${{ github.event.pull_request.number }} | ||||
|           body: | | ||||
|             **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:** | ||||
|             - [Report](https://update.flipperzero.one/builds/firmware-pvs-studio-report/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) | ||||
|             - [Report](https://pvs.flipp.dev/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) | ||||
|           edit-mode: replace | ||||
| 
 | ||||
|       - name: 'Raise exception' | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| App( | ||||
|     appid="example_custom_font", | ||||
|     name="Example: custom font", | ||||
|     apptype=FlipperAppType.EXTERNAL, | ||||
|     apptype=FlipperAppType.DEBUG, | ||||
|     entry_point="example_custom_font_main", | ||||
|     requires=["gui"], | ||||
|     stack_size=1 * 1024, | ||||
|  | ||||
| @ -115,8 +115,12 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { | ||||
| 
 | ||||
|     if(furi_hal_usb_is_locked()) { | ||||
|         app->error = BadUsbAppErrorCloseRpc; | ||||
|         app->usb_if_prev = NULL; | ||||
|         scene_manager_next_scene(app->scene_manager, BadUsbSceneError); | ||||
|     } else { | ||||
|         app->usb_if_prev = furi_hal_usb_get_config(); | ||||
|         furi_check(furi_hal_usb_set_config(NULL, NULL)); | ||||
| 
 | ||||
|         if(!furi_string_empty(app->file_path)) { | ||||
|             app->bad_usb_script = bad_usb_script_open(app->file_path); | ||||
|             bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout); | ||||
| @ -138,6 +142,10 @@ void bad_usb_app_free(BadUsbApp* app) { | ||||
|         app->bad_usb_script = NULL; | ||||
|     } | ||||
| 
 | ||||
|     if(app->usb_if_prev) { | ||||
|         furi_check(furi_hal_usb_set_config(app->usb_if_prev, NULL)); | ||||
|     } | ||||
| 
 | ||||
|     // Views
 | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); | ||||
|     bad_usb_free(app->bad_usb_view); | ||||
|  | ||||
| @ -14,6 +14,7 @@ | ||||
| #include <gui/modules/variable_item_list.h> | ||||
| #include <gui/modules/widget.h> | ||||
| #include "views/bad_usb_view.h" | ||||
| #include <furi_hal_usb.h> | ||||
| 
 | ||||
| #define BAD_USB_APP_BASE_FOLDER ANY_PATH("badusb") | ||||
| #define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/assets/layouts" | ||||
| @ -39,6 +40,8 @@ struct BadUsbApp { | ||||
|     FuriString* keyboard_layout; | ||||
|     BadUsb* bad_usb_view; | ||||
|     BadUsbScript* bad_usb_script; | ||||
| 
 | ||||
|     FuriHalUsbInterface* usb_if_prev; | ||||
| }; | ||||
| 
 | ||||
| typedef enum { | ||||
|  | ||||
| @ -490,8 +490,6 @@ static int32_t bad_usb_worker(void* context) { | ||||
|     BadUsbWorkerState worker_state = BadUsbStateInit; | ||||
|     int32_t delay_val = 0; | ||||
| 
 | ||||
|     FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); | ||||
| 
 | ||||
|     FURI_LOG_I(WORKER_TAG, "Init"); | ||||
|     File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); | ||||
|     bad_usb->line = furi_string_alloc(); | ||||
| @ -642,8 +640,6 @@ static int32_t bad_usb_worker(void* context) { | ||||
| 
 | ||||
|     furi_hal_hid_set_state_callback(NULL, NULL); | ||||
| 
 | ||||
|     furi_hal_usb_set_config(usb_mode_prev, NULL); | ||||
| 
 | ||||
|     storage_file_close(script_file); | ||||
|     storage_file_free(script_file); | ||||
|     furi_string_free(bad_usb->line); | ||||
|  | ||||
| @ -26,7 +26,7 @@ void nfc_magic_scene_wrong_card_on_enter(void* context) { | ||||
|         AlignLeft, | ||||
|         AlignTop, | ||||
|         FontSecondary, | ||||
|         "Writing is supported\nonly for 4 bytes UID\nMifare CLassic 1k"); | ||||
|         "Writing is supported\nonly for 4 bytes UID\nMifare Classic 1k"); | ||||
|     widget_add_button_element( | ||||
|         widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wrong_card_widget_callback, nfc_magic); | ||||
| 
 | ||||
|  | ||||
| @ -58,8 +58,12 @@ bool updater_scene_error_on_event(void* context, SceneManagerEvent event) { | ||||
| } | ||||
| 
 | ||||
| void updater_scene_error_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
|     Updater* updater = (Updater*)context; | ||||
| 
 | ||||
|     widget_reset(updater->widget); | ||||
|     free(updater->pending_update); | ||||
| 
 | ||||
|     if(updater->loaded_manifest) { | ||||
|         update_manifest_free(updater->loaded_manifest); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -21,11 +21,9 @@ void updater_scene_loadcfg_apply_callback(GuiButtonType result, InputType type, | ||||
| 
 | ||||
| void updater_scene_loadcfg_on_enter(void* context) { | ||||
|     Updater* updater = (Updater*)context; | ||||
|     UpdaterManifestProcessingState* pending_upd = updater->pending_update = | ||||
|         malloc(sizeof(UpdaterManifestProcessingState)); | ||||
|     pending_upd->manifest = update_manifest_alloc(); | ||||
|     UpdateManifest* loaded_manifest = updater->loaded_manifest = update_manifest_alloc(); | ||||
| 
 | ||||
|     if(update_manifest_init(pending_upd->manifest, furi_string_get_cstr(updater->startup_arg))) { | ||||
|     if(update_manifest_init(loaded_manifest, furi_string_get_cstr(updater->startup_arg))) { | ||||
|         widget_add_string_element( | ||||
|             updater->widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, "Update"); | ||||
| 
 | ||||
| @ -37,7 +35,7 @@ void updater_scene_loadcfg_on_enter(void* context) { | ||||
|             32, | ||||
|             AlignCenter, | ||||
|             AlignCenter, | ||||
|             furi_string_get_cstr(pending_upd->manifest->version), | ||||
|             furi_string_get_cstr(loaded_manifest->version), | ||||
|             true); | ||||
| 
 | ||||
|         widget_add_button_element( | ||||
| @ -95,13 +93,12 @@ bool updater_scene_loadcfg_on_event(void* context, SceneManagerEvent event) { | ||||
| } | ||||
| 
 | ||||
| void updater_scene_loadcfg_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
|     Updater* updater = (Updater*)context; | ||||
| 
 | ||||
|     if(updater->pending_update) { | ||||
|         update_manifest_free(updater->pending_update->manifest); | ||||
|         furi_string_free(updater->pending_update->message); | ||||
|     } | ||||
| 
 | ||||
|     widget_reset(updater->widget); | ||||
|     free(updater->pending_update); | ||||
| 
 | ||||
|     if(updater->loaded_manifest) { | ||||
|         update_manifest_free(updater->loaded_manifest); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -33,12 +33,6 @@ typedef enum { | ||||
|     UpdaterCustomEventSdUnmounted, | ||||
| } UpdaterCustomEvent; | ||||
| 
 | ||||
| typedef struct UpdaterManifestProcessingState { | ||||
|     UpdateManifest* manifest; | ||||
|     FuriString* message; | ||||
|     bool ready_to_be_applied; | ||||
| } UpdaterManifestProcessingState; | ||||
| 
 | ||||
| typedef struct { | ||||
|     // GUI
 | ||||
|     Gui* gui; | ||||
| @ -49,7 +43,7 @@ typedef struct { | ||||
| 
 | ||||
|     UpdaterMainView* main_view; | ||||
| 
 | ||||
|     UpdaterManifestProcessingState* pending_update; | ||||
|     UpdateManifest* loaded_manifest; | ||||
|     UpdatePrepareResult preparation_result; | ||||
| 
 | ||||
|     UpdateTask* update_task; | ||||
|  | ||||
| @ -41,22 +41,22 @@ typedef struct { | ||||
| static const UpdateTaskStageGroupMap update_task_stage_progress[] = { | ||||
|     [UpdateTaskStageProgress] = STAGE_DEF(UpdateTaskStageGroupMisc, 0), | ||||
| 
 | ||||
|     [UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), | ||||
|     [UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 15), | ||||
|     [UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 45), | ||||
|     [UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), | ||||
| 
 | ||||
|     [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 15), | ||||
|     [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), | ||||
|     [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 80), | ||||
|     [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), | ||||
|     [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 80), | ||||
|     [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 35), | ||||
|     [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), | ||||
|     [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 30), | ||||
|     [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 5), | ||||
| 
 | ||||
|     [UpdateTaskStageOBValidation] = STAGE_DEF(UpdateTaskStageGroupOptionBytes, 10), | ||||
|     [UpdateTaskStageOBValidation] = STAGE_DEF(UpdateTaskStageGroupOptionBytes, 2), | ||||
| 
 | ||||
|     [UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 50), | ||||
|     [UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 200), | ||||
|     [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 30), | ||||
|     [UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 30), | ||||
|     [UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 150), | ||||
|     [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 15), | ||||
| 
 | ||||
|     [UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 30), | ||||
|     [UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 5), | ||||
| 
 | ||||
|     [UpdateTaskStageResourcesUpdate] = STAGE_DEF(UpdateTaskStageGroupResources, 255), | ||||
|     [UpdateTaskStageSplashscreenInstall] = STAGE_DEF(UpdateTaskStageGroupSplashscreen, 5), | ||||
|  | ||||
| @ -41,6 +41,14 @@ static bool update_task_pre_update(UpdateTask* update_task) { | ||||
|     return success; | ||||
| } | ||||
| 
 | ||||
| typedef enum { | ||||
|     UpdateTaskResourcesWeightsFileCleanup = 20, | ||||
|     UpdateTaskResourcesWeightsDirCleanup = 20, | ||||
|     UpdateTaskResourcesWeightsFileUnpack = 60, | ||||
| } UpdateTaskResourcesWeights; | ||||
| 
 | ||||
| #define UPDATE_TASK_RESOURCES_FILE_TO_TOTAL_PERCENT 90 | ||||
| 
 | ||||
| typedef struct { | ||||
|     UpdateTask* update_task; | ||||
|     int32_t total_files, processed_files; | ||||
| @ -54,33 +62,36 @@ static bool update_task_resource_unpack_cb(const char* name, bool is_directory, | ||||
|     update_task_set_progress( | ||||
|         unpack_progress->update_task, | ||||
|         UpdateTaskStageProgress, | ||||
|         /* For this stage, last 70% of progress = extraction */ | ||||
|         30 + (unpack_progress->processed_files * 70) / (unpack_progress->total_files + 1)); | ||||
|         /* For this stage, last progress segment = extraction */ | ||||
|         (UpdateTaskResourcesWeightsFileCleanup + UpdateTaskResourcesWeightsDirCleanup) + | ||||
|             (unpack_progress->processed_files * UpdateTaskResourcesWeightsFileUnpack) / | ||||
|                 (unpack_progress->total_files + 1)); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|     update_task_cleanup_resources(UpdateTask* update_task, uint32_t n_approx_file_entries) { | ||||
| static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_t n_tar_entries) { | ||||
|     ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(update_task->storage); | ||||
|     do { | ||||
|         FURI_LOG_I(TAG, "Cleaning up old manifest"); | ||||
|         FURI_LOG_D(TAG, "Cleaning up old manifest"); | ||||
|         if(!resource_manifest_reader_open(manifest_reader, EXT_PATH("Manifest"))) { | ||||
|             FURI_LOG_W(TAG, "No existing manifest"); | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         /* We got # of entries in TAR file. Approx 1/4th is dir entries, we skip them */ | ||||
|         n_approx_file_entries = n_approx_file_entries * 3 / 4 + 1; | ||||
|         uint32_t n_processed_files = 0; | ||||
|         const uint32_t n_approx_file_entries = | ||||
|             n_tar_entries * UPDATE_TASK_RESOURCES_FILE_TO_TOTAL_PERCENT / 100 + 1; | ||||
|         uint32_t n_dir_entries = 1; | ||||
| 
 | ||||
|         ResourceManifestEntry* entry_ptr = NULL; | ||||
|         uint32_t n_processed_entries = 0; | ||||
|         while((entry_ptr = resource_manifest_reader_next(manifest_reader))) { | ||||
|             if(entry_ptr->type == ResourceManifestEntryTypeFile) { | ||||
|                 update_task_set_progress( | ||||
|                     update_task, | ||||
|                     UpdateTaskStageProgress, | ||||
|                     /* For this stage, first 20% of progress = cleanup files */ | ||||
|                     (n_processed_files++ * 20) / (n_approx_file_entries + 1)); | ||||
|                     /* For this stage, first pass = old manifest's file cleanup */ | ||||
|                     (n_processed_entries++ * UpdateTaskResourcesWeightsFileCleanup) / | ||||
|                         n_approx_file_entries); | ||||
| 
 | ||||
|                 FuriString* file_path = furi_string_alloc(); | ||||
|                 path_concat( | ||||
| @ -88,16 +99,21 @@ static void | ||||
|                 FURI_LOG_D(TAG, "Removing %s", furi_string_get_cstr(file_path)); | ||||
|                 storage_simply_remove(update_task->storage, furi_string_get_cstr(file_path)); | ||||
|                 furi_string_free(file_path); | ||||
|             } else if(entry_ptr->type == ResourceManifestEntryTypeDirectory) { | ||||
|                 n_dir_entries++; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         n_processed_entries = 0; | ||||
|         while((entry_ptr = resource_manifest_reader_previous(manifest_reader))) { | ||||
|             if(entry_ptr->type == ResourceManifestEntryTypeDirectory) { | ||||
|                 update_task_set_progress( | ||||
|                     update_task, | ||||
|                     UpdateTaskStageProgress, | ||||
|                     /* For this stage, second 10% of progress = cleanup directories */ | ||||
|                     (n_processed_files++ * 10) / (n_approx_file_entries + 1)); | ||||
|                     UpdateTaskResourcesWeightsFileCleanup + | ||||
|                         (n_processed_entries++ * UpdateTaskResourcesWeightsDirCleanup) / | ||||
|                             n_dir_entries); | ||||
| 
 | ||||
|                 FuriString* folder_path = furi_string_alloc(); | ||||
|                 File* folder_file = storage_file_alloc(update_task->storage); | ||||
|  | ||||
| @ -340,7 +340,7 @@ static void usb_process_mode_start(FuriHalUsbInterface* interface, void* context | ||||
| } | ||||
| 
 | ||||
| static void usb_process_mode_change(FuriHalUsbInterface* interface, void* context) { | ||||
|     if(interface != usb.interface) { | ||||
|     if((interface != usb.interface) || (context != usb.interface_context)) { | ||||
|         if(usb.enabled) { | ||||
|             // Disable current interface
 | ||||
|             susp_evt(&udev, 0, 0); | ||||
|  | ||||
| @ -237,10 +237,8 @@ void ibutton_worker_emulate_timer_cb(void* context) { | ||||
|     const LevelDuration level_duration = | ||||
|         protocol_dict_encoder_yield(worker->protocols, worker->protocol_to_encode); | ||||
| 
 | ||||
|     const bool level = level_duration_get_level(level_duration); | ||||
| 
 | ||||
|     furi_hal_ibutton_emulate_set_next(level); | ||||
|     furi_hal_ibutton_pin_write(level); | ||||
|     furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level_duration)); | ||||
|     furi_hal_ibutton_pin_write(level_duration_get_level(level_duration)); | ||||
| } | ||||
| 
 | ||||
| void ibutton_worker_emulate_timer_start(iButtonWorker* worker) { | ||||
|  | ||||
| @ -106,6 +106,7 @@ void tar_archive_set_file_callback(TarArchive* archive, tar_unpack_file_cb callb | ||||
| static int tar_archive_entry_counter(mtar_t* tar, const mtar_header_t* header, void* param) { | ||||
|     UNUSED(tar); | ||||
|     UNUSED(header); | ||||
|     furi_assert(param); | ||||
|     int32_t* counter = param; | ||||
|     (*counter)++; | ||||
|     return 0; | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| from SCons.Builder import Builder | ||||
| from SCons.Action import Action | ||||
| from SCons.Script import Delete, Mkdir, GetBuildFailures | ||||
| from SCons.Script import Delete, Mkdir, GetBuildFailures, Flatten | ||||
| import multiprocessing | ||||
| import webbrowser | ||||
| import atexit | ||||
| @ -30,13 +30,14 @@ def atexist_handler(): | ||||
|         return | ||||
| 
 | ||||
|     for bf in GetBuildFailures(): | ||||
|         if bf.node.exists and bf.node.name.endswith(".html"): | ||||
|             # macOS | ||||
|             if sys.platform == "darwin": | ||||
|                 subprocess.run(["open", bf.node.abspath]) | ||||
|             else: | ||||
|                 webbrowser.open(bf.node.abspath) | ||||
|             break | ||||
|         for node in Flatten(bf.node): | ||||
|             if node.exists and node.name.endswith(".html"): | ||||
|                 # macOS | ||||
|                 if sys.platform == "darwin": | ||||
|                     subprocess.run(["open", node.abspath]) | ||||
|                 else: | ||||
|                     webbrowser.open(node.abspath) | ||||
|                 break | ||||
| 
 | ||||
| 
 | ||||
| def generate(env): | ||||
|  | ||||
| @ -17,7 +17,7 @@ def parse_args(): | ||||
| 
 | ||||
| 
 | ||||
| def checkCommitMessage(msg): | ||||
|     regex = re.compile(r"^'?\[FL-\d+\]") | ||||
|     regex = re.compile(r"^'?\[(FL-\d+,?\s?)+\]") | ||||
|     if regex.match(msg): | ||||
|         return True | ||||
|     return False | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandr Kutuzov
						Aleksandr Kutuzov