Skip to Content
Technical Articles

Create a commit in Git when an ABAP task is released

If you already looked into Git-enabled CTS (gCTS), you might know that new commits are created in the standard as soon as you release a transport request.

But what if this is not possible or not helpful in your case? It might be that many developers work on one transport request – each one stores changed objects in a task that is part of that transport request. But each developer might want to commit and test his changes when he feels it is a good point in time for doing so. He might not want to wait for all the others to finish their tasks so that the whole transport request can be released.

So how about committing on a task level to enable individual progress and testing for each developer?

It is possible. The solution is to implement the ‘CHECK_BEFORE_RELEASE’-method of BAdI CTS_REQUEST_CHECK.

In the following description on how to do this, you will find some sample coding that you can use but that we provide without any further warranty or official SAP support.

In the example given below, we will use the userID (so ‘SY-UNAME’) and the package name as conditions to decide whether a commit shall be created on task level or on request level. Other criteria are for sure possible. You could e.g. use just the package name(s) – without any dependency on the user – or even check whether a certain object is already part of a cloned repository, or you can completely leave out these conditions if you want to always use the commit option on task level.

In any case, make sure that the condition is met for every user, or for every transport task, or for whatever criterion that you choose – but nothing more, nothing less. So, it might happen that you have to adapt the condition from time to time in case new users join this working mode or additional packages shall make use of gCTS.

Let’s go!

  1. Open transaction SE18 in your ABAP development system and search for BAdI ‘CTS_REQUEST_CHECK’.
  2. Choose Display, and switch to the tab Interfaces to view the details:
    BAdI%20CTS_REQUEST_CHECKThe method that we need to implement is ‘CHECK_BEFORE_RELEASE’. Other methods of the standard interface are not relevant in this case. If you already implemented this method or other ones of this BAdI, make sure that implementations do not contradict or overwrite each other.
  3. Choose Implementation -> Create.
    Create%20BAdI
  4. Enter an Implementation Name – e.g. Z_GCTS_REQUEST_CHECK.
    Implementation%20NameClick on Continue (Enter) – the green check mark.
  5. On the next screen, enter an Implementation Short Text.
    If you like, you can also change the Name of Implementing Class. In the example given in here, I did that:
    Implementation%20DetailsLeave ABAP Code as Implementation Type.
    Save your changes.
    In the dialog popping up, you can add the implementation to a package. As I need the implementation only on my development system and don’t want to transport it, I chose to store it as local object.
    Save%20BAdI
  6. Double click on name of the implementing class:
    Start%20Class%20Implementation
  7. The class builder opens up.
    Go to Local Definitions/Implementations. Click on Source Code Based.Class%20Builder
  8. Copy the coding that you can find at the end of this blog into the editor.
    Source%20Code
    Please note: The way the object list is used in the method get_objects_to_push with the implementation provided in the sample coding below only works for GitHub. If you use another collaborative version control software based on Git, please check the APIs provided by your vendor to find out how this method could be implemented. Adapt the coding to your needs. If you use it ‘just like it is’, your implementation will not work.
    You can find some comments in the coding that will hopefully help you understand it and that explain what is required to be done if you want to commit on task level. The most important thing to do is that you adapt the basic conditions, when the commit on task level shall be executed:

    • The line if sy-uname = ‘MY_USER (in the method if_ex_cts_request_check~check_before_release) restricts the option to release on task level to the user-ID MY_USER. Adapt this line to your needs. You can e.g. replace ‘MY_USER’ with the userID(s) that should commit on task level, or use another condition that fits to your needs, or remove the condition completely if you would like to enable commits on task level in any case (if so, remove the corresponding ‘endif’ as well).
    • The line constants co_superpackage type string value ‘MY_TADIR_DEVC_PACKAGE‘ in the public section of the class definition restricts releasing on task level to the package MY_TADIR_DEVC_PACKAGE. Replace that by your package name or change the respective coding according to your needs.
  9. Check, save, and activate your coding.CheckActivate
  10. If everything is ok, click Back twice until you have reached the initial screen of your BAdI.
    Back
  11. In there, you should still see the Runtime Behavior ‘Implementation will not be called’. Click Activate business add-in Implementation as soon as you want to make use of the functionality.
    ActivateBAdI

 

You are done – try releasing a task where your conditions are met and check that a commit gets created.

 

Tips & Tricks

  • When you release a task, a commit will now automatically be created. The commit message will contain a transport request number, name, and /or ID. You have most probably never seen this transport request number before. It is the number of a transport of copies that has been automatically created in the background to allow exporting the task. You can change the commit message to something more meaningful by maintaining the parameters CLIENT_VCS_COMMIT_DESCRIPTION and CLIENT_VCS_COMMIT_MESSAGE for your repository. For more details, please take a look at the SAP Help Portal at Configuration Parameters for Repositories
  • If your implementation seems not to work as e.g. there is no commit visible in the commits list after you have released a task that fulfills your conditions, check the Log tab for your repository in the gCTS Fiori app.Commits
  • Commits and pushes are executed with the help of a job in transaction SM37. In case of issues, it might also be worth checking whether the job is there and (has been) executed: Choose the event SAP_TRIGGER_VCS_IMPORT and the ABAP Program Name SCTS_ABAP_VCS_IMPORT_OBSERVER and click Execute.ScheduledJobYou should get a list showing when the job was executed. If this is not the case, check your gCTS configuration. More details are provided on the SAP Help Portal under Enable Git-Enabled Change and Transport System in an ABAP System.

 

Sample Implementation

Please make sure that you copy the complete coding – the lines are longer than shown directly.

class zcl_im_cl_cts_git_forward definition
  public
  final
  create public .

  public section.

    types ty_tadir type standard table of tadir with default key.
    types ty_objects_to_push type table of if_cts_abap_vcs_transport_req=>ty_objects with default key.

    interfaces if_ex_cts_request_check .

    constants co_superpackage type string value 'MY_TADIR_DEVC_PACKAGE' ##NO_TEXT.
  protected section.
  private section.

    methods is_my_package_package
      importing
        !iv_package     type devclass
      returning
        value(rv_found) type boolean .

    methods get_objects_to_push
      importing
        !iv_repository            type    if_cts_abap_vcs_repository=>ty_repository_json
        !it_object_list           type ty_tadir
      returning
        value(rt_objects_to_push) type ty_objects_to_push .

endclass.



class zcl_im_cl_cts_git_forward implementation.

  method if_ex_cts_request_check~check_before_add_objects.
    " Not needed
  endmethod.


  method if_ex_cts_request_check~check_before_changing_owner.
    " Not needed
  endmethod.


  method if_ex_cts_request_check~check_before_creation.
    " Not needed
  endmethod.


  method if_ex_cts_request_check~check_before_release.

    data: ls_tadir       type tadir,
          ls_e071        type e071,
          lt_e071        type table of e071 with default key,
          lt_object_list type ty_tadir,
          ls_address     type bapiaddr3,
          lv_objname     type sobj_name,
          lv_check       type flag.

    " Execute just for a specific user (optional)
    if sy-uname = 'MY_USER'.

      "   Execute just for certain TR type (e.g. task)
      "   R - repair, S - development/correction,  Q -customizing task
      if type = 'R' or type = 'S' or type = 'Q'.

        " loop over provided objects from the BAdI signature parameter
        loop at objects into data(ls_object).
          data(lv_pgmid) = ls_object-pgmid.
          data(lv_obj_type) = ls_object-object.
          lv_objname = ls_object-obj_name.

          " Find the super object for part objects which have not pgmid = R3TR
          " This is needed if that object will be committed for the very first time
          " Otherwise the repository will just contain, e.g. a single method, which cannot be deployed to a target system
          if ls_object-pgmid = 'LIMU'.
            call function 'TR_CHECK_TYPE'
              exporting
                wi_e071  = ls_object
              importing
                we_tadir = ls_tadir
              exceptions
                others   = 1.

            " There was a TADIR entry for that object
            if sy-subrc = 0.
              lv_pgmid = ls_tadir-pgmid.
              lv_obj_type = ls_tadir-object.
              lv_objname = ls_tadir-obj_name.
              clear ls_tadir.
            else.
              continue.
            endif.
          endif.

          " Also keep sure that this is a 'real' object and not a generated one
          call function 'DEV_CHECK_OBJECT_EXISTS'
            exporting
              i_pgmid   = lv_pgmid
              i_objtype = lv_obj_type
              i_objname = lv_objname
            importing
              e_exists  = lv_check.

          if sy-subrc = 0.
            if lv_check = abap_true.

              " Double check that the related object could be transported by SE01/SE09
              call function 'TR_TADIR_INTERFACE'
                exporting
                  wi_tadir_pgmid    = lv_pgmid
                  wi_tadir_object   = lv_obj_type
                  wi_tadir_obj_name = lv_objname
                  wi_read_only      = 'X'
                importing
                  new_tadir_entry   = ls_tadir
                exceptions
                  others            = 1.

              " Evaluate that the current object is related to my DEVC package (optional)
              " Why do we need that?
              " It could be the case that we have a lot of different TR which will be transported trought our STMS landscape
              " In the case that gCTS will be used in parallel with common CTS we have to distinguish between the various object package relation
              " because of the fact that we probably just want to push objects which are related to our repository
              " For S/4HANA 2020: There is method which can be used in order to determine repository relation:
              " CL_CTS_ABAP_VCS_ORGANIZER_FAC=>check_objects
              if is_my_package_package( ls_tadir-devclass ) = abap_true and ls_tadir-genflag = abap_false.
                if not line_exists( lt_object_list[ obj_name = ls_tadir-obj_name ] ).
                  append ls_tadir to lt_object_list.
                  clear ls_tadir.
                endif.
              endif.
            else.
              continue.
            endif.
          else.
            " Handle Exception Here
            raise cancel.
          endif.
        endloop.

        " Get all exisiting repositories for current system
        data(lt_repositories) = cl_cts_abap_vcs_api_facade=>get_repositories( value #( ) ).

        " Was everything okay?
        if lt_repositories-exception is initial.
          loop at lt_repositories-result into data(ls_repository).

            " Compare provided object list with objects list of repository ...
            data(lt_objects_to_push) = get_objects_to_push( iv_repository = ls_repository it_object_list = lt_object_list ).
            if lt_objects_to_push is not initial.

              " Consume simplified facade method in order to commit&push objects to remote repository
              data(lv_response) = cl_cts_abap_vcs_trans_facade=>push_objects( value #(
                " Define Commit message
                desc = text
                " Define repository id for related objects
                repository = ls_repository-rid
                " Define repository related objects
                objects = lt_objects_to_push
              ) ).
            endif.
          endloop.
        endif.

      endif.
    endif.

  endmethod.


  method if_ex_cts_request_check~check_before_release_slin.
  endmethod.


  method is_my_package_package.

    " Is this our wanted package?
    if iv_package = co_superpackage.
      rv_found = abap_true.
    else.

      " Load package method information
      call method cl_package=>load_package
        exporting
          i_package_name = iv_package
        importing
          e_package      = data(lo_package)
        exceptions
          others         = 1.
      if lo_package is not initial.

        " is there a superpackage?
        " If not the process is over and this isn't our wanted package
        if lo_package->super_package_name is initial.
          rv_found = abap_false.
        elseif lo_package->super_package_name = co_superpackage .
          rv_found = abap_true.
        else.

          " Recursive call if there is a superpackage which also have to be evaluated
          rv_found = is_my_package_package( lo_package->super_package_name  ).
        endif.
      else.
        "this isn't our wanted package
        rv_found = abap_false.
      endif.
    endif.
  endmethod.


  method get_objects_to_push.

    data: ls_single_object type if_cts_abap_vcs_repository=>ty_object.

    " Get all available objects for repository (Warning: this will just work with GitHub API repositories!)
    data(ls_objects_response) = cl_cts_abap_vcs_repo_facade=>get_objects( iv_repository-rid ).

    if ls_objects_response-objects is not initial and ls_objects_response-exception is initial.
      loop at it_object_list into data(ls_object).
        if line_exists( ls_objects_response-objects[ object = ls_object-devclass type = 'DEVC' ] ) or
            line_exists( ls_objects_response-objects[ object = co_superpackage type = 'DEVC' ] ).

          if line_exists( ls_objects_response-objects[ object = ls_object-obj_name type = ls_object-object ] ).

            ls_single_object =  ls_objects_response-objects[ object = ls_object-obj_name type = ls_object-object ].
            append value #( object = ls_single_object-object type = ls_single_object-type ) to rt_objects_to_push.
          else.
            append value #( object = ls_object-obj_name type = ls_object-object ) to rt_objects_to_push.
          endif.

        elseif line_exists( ls_objects_response-objects[ object = ls_object-obj_name type = ls_object-object ] ).
          ls_single_object =  ls_objects_response-objects[ object = ls_object-obj_name type = ls_object-object ].
          append value #( object = ls_single_object-object type = ls_single_object-type ) to rt_objects_to_push.

        endif.
        clear ls_single_object.
      endloop.
    endif.
  endmethod.
endclass.

 

Hope that you find this useful. If you find issues or have questions or suggestions, feel free to post them as comments to this blog post.

Be the first to leave a comment
You must be Logged on to comment or reply to a post.