github.com/quickfeed/quickfeed@v0.0.0-20240507093252-ed8ca812a09c/doc/dev.md (about)

     1  # QuickFeed Developer Guide
     2  
     3  This developer guide assumes that you have [installed and configured QuickFeed](./deploy.md) and its dependent components.
     4  
     5  ## GitHub Integration
     6  
     7  - [GitHub Application Setup](./github.md)
     8  - [Setting up Course Organization](./teacher.md)
     9  
    10  ## Tools
    11  
    12  QuickFeed provides a few command line tools.
    13  See [cmd/scm/README.md](cmd/scm/README.md) for documentation of the SCM tool.
    14  
    15  ## Makefile
    16  
    17  The Makefile in QuickFeed simplifies various tasks like compiling, updating, and launching the server.
    18  
    19  ### Compiling Targets
    20  
    21  After modifying qf/qf.proto, you need to recompile both frontend and backend. Use the following commands:
    22  
    23  ```sh
    24  make proto
    25  ```
    26  
    27  To recompile and install the QuickFeed server, run:
    28  
    29  ```sh
    30  make install
    31  ```
    32  
    33  To compile the browser client bundles:
    34  
    35  ```sh
    36  make ui
    37  ```
    38  
    39  ### Testing
    40  
    41  To run all tests that does not require remote interactions.
    42  
    43  ```sh
    44  make test
    45  ```
    46  
    47  To run specific tests that requires remote interactions with GitHub you must create a personal access token and assign it to `GITHUB_ACCESS_TOKEN`:
    48  
    49  ```sh
    50  export GITHUB_ACCESS_TOKEN=<your-personal-access-token>
    51  ```
    52  
    53  Some other tests may also require access to a specific test course organization; for these tests use `QF_TEST_ORG`:
    54  
    55  ```sh
    56  export QF_TEST_ORG=<your-test-course-organization>
    57  ```
    58  
    59  Here are some examples of such tests:
    60  
    61  ```sh
    62  cd assignments
    63  go test -v -run TestFetchAssignments
    64  cd ci
    65  go test -v -run TestRunTests
    66  cd scm
    67  go test -v -run TestGetOrganization
    68  go test -v -run TestListHooks
    69  QF_WEBHOOK_SERVER=https://62b9b9c05ece.ngrok.io go test -v -run TestCreateHook
    70  go test -v -run TestListHooks
    71  cd web/hooks
    72  QF_WEBHOOK_SERVER=https://62b9b9c05ece.ngrok.io go test -v -run TestGitHubWebHook
    73  ```
    74  
    75  ## Server architecture
    76  
    77  ### Default Configuration
    78  
    79  TODO(meling) Update and improve this part. It is not correct anymore, I think.
    80  
    81  - **Primary Server Port:** 
    82  By default, the server runs on port **:443**, the standard port for HTTPS traffic. This ensures secure communication right out of the box.
    83  - **Custom Port Configuration:** 
    84  If you need to use a different port, you can easily change this by using the `-http.addr` flag when launching the server.
    85  - **HTTP to HTTPS Redirection:** 
    86  Alongside the main server, we also initiate a secondary server on port **:80**. Its sole purpose is to redirect all incoming HTTP requests to HTTPS. 
    87  This ensures that even if someone attempts to connect via the unsecured HTTP protocol, their request will be automatically upgraded to a secure connection.
    88  
    89  **Important Note:** 
    90  When running servers on ports like **:80** or **:443**, some operating systems may require elevated permissions or specific configurations. 
    91  This is because ports below 1024 are considered privileged ports, and running services on these ports might need administrative rights or special configurations.
    92  
    93  In Linux, you can use the `setcap` command to allow a binary to bind to privileged ports without elevated permissions. 
    94  For example, to allow the `quickfeed` binary to bind to port **:443**, you can run the following command:
    95  
    96  ```sh
    97  sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary/quickfeed
    98  ```
    99  
   100  Note that you will need to repeat this step each time you recompile the server.
   101  
   102  Please make sure to check your operating system's documentation for the necessary steps to run services on these ports.
   103  
   104  ## Errors and logging
   105  
   106  Application errors can be classified into several groups and handled in different ways:
   107  
   108  **Database errors**:
   109    - Return generic "not found/failed" error message to the user, log the original error.
   110  
   111  **SCM errors**:
   112  
   113  - Some of these can only be fixed by the user who is calling the method by interacting with UI elements (usually course teacher).
   114  
   115    **Examples**: 
   116    - If a GitHub organization cannot be found, one of the possible issues causing this behavior is not having installed the GitHub application on the organization. 
   117    As a result, the requested organization cannot be seen by QuickFeed. 
   118    - If a GitHub repository or team cannot be found, they could have been manually deleted from GitHub. 
   119    Only the current user can remedy the situation, and it is most useful to inform them about the issue in detail and offer a solution.
   120  
   121  - Sometimes GitHub interactions take too long and the request times out, or is otherwise cancelled by GitHub.
   122  In these cases the error is usually ephemeral in nature, and the action should be repeated at later time. This should be communicated to the end user.
   123  
   124  - Return a custom error with detailed information for logging, and a custom error message to the user.
   125  
   126  **Access control errors**:
   127  
   128  - Return generic "access denied" error message to the user.
   129  
   130  **API errors (invalid requests)**:
   131  
   132  - Return generic "malformed request" message to the user.
   133  
   134  **GitHub API errors (request struct has missing/malformed fields)**
   135  
   136  - Return a custom error with detailed information for logging and generic "failed precondition" message to the user.
   137  
   138  
   139  [Connect Error Codes](https://connectrpc.com/docs/protocol#error-codes) are used to allow the client to check whether the error message should be displayed for user, or just logged for developers.
   140  
   141  ### Backend
   142  
   143  Errors are being logged at `QuickFeed Service` level. 
   144  All other methods called from there (including database and SCM methods) will just wrap and return all error messages directly. 
   145  Introduce logging on layers deeper than `QuickFeed Service` only if necessary.
   146  
   147  Errors returned to a user should be few and informative. 
   148  They should not reveal internal details of the application.
   149  
   150  ### Frontend
   151  
   152  When receiving a response from the server, the response status code is checked on the frontend. 
   153  Any message with code different from 0 (0 is status code `OK`) will be logged to console. 
   154  Error messages will be displayed to user where relevant, e.g. on course and group creation, and user and group enrollment updates.
   155  
   156  [Connect Error Codes](https://connectrpc.com/docs/protocol#error-codes)
   157  
   158  ## GitHub API
   159  
   160  For GitHub integration we are using [Go implementation](https://github.com/google/go-github) of [GitHub API](https://docs.github.com/en/rest)
   161  
   162  ### Webhooks
   163  
   164  - GitHub [Webhooks API](https://docs.github.com/en/webhooks) is used for building and testing of code submitted by students.
   165  - A webhook is created automatically when installing the GitHub App on a course organization. The webhook will be triggered by pushes to repositories in the organization.
   166  - Push events from the `tests` repository may update
   167    - The assignment information in QuickFeed's database.
   168    - The Docker container and run.sh script used for building and testing student submitted code.
   169  - Push events from the `username-labs` repositories may trigger text execution.
   170  - The webhook will POST events to `$DOMAIN/hook/`, where `$DOMAIN` is the domain name of the server, as defined in your `.env` file.
   171  
   172  ### User roles/access levels for organization / team / repository
   173  
   174  - GitHub API name for organization owner is `admin`
   175  - Repository access levels for any organization member in GitHub API calls are: `read`/`write`/`admin`/`none`
   176  - Individual repository permission levels in GitHub API are: `pull`/`push`/`admin`
   177  
   178  ### Slugs
   179  
   180  When retrieving team, organization or repository by name, GitHub expects a slugified string instead of a full name as displayed on the organization page.
   181  For example, organization with a name like `QuickFeed Test Org` will have slugified name `quickfeed-test-org`.
   182  
   183  [URL slugs explained](http://patterns.dataincubator.org/book/url-slug.html)
   184  
   185  ### Repositories
   186  
   187  - `owner` field for any organization repository is a slugified name for that organization
   188  - access policy:
   189    - on course creation - default repository access across the whole organization is set to `none`, which means that only the organization owners can see any private repository on that organization
   190    - when students enroll, they receive read/pull access to `assignments` repository and write/push access to a personal student repository as GitHub invitations to their registered GitHub email
   191  
   192  ### Teams
   193  
   194  QuickFeed will create a team for each group in a course organization. 
   195  The team name will be the same as the group name. 
   196  Members of a group will be added to the corresponding team, and will have write/push access to the group repository.
   197  
   198  Group records in the database will have references to the corresponding GitHub team ID's.
   199  
   200  ## Docker
   201  
   202  QuickFeed will build code submitted by students, and run tests provided by teachers inside docker containers.
   203  An often encountered problem is Docker being unable to resolve DNS due to disabled public DNS.
   204  If you get a build error like that:
   205  
   206  ```log
   207  Docker execution failed{error 25 0  Error response from daemon: Get https://registry-1.docker.io/v2/: dial tcp: lookup registry-1.docker.io on [::1]:53: read udp [::1]:50111->[::1]:53: read: connection refused}
   208  ```
   209  
   210  then it must be a DNS problem.
   211  
   212  One of the solutions is to uncomment or change `DOCKER_OPTS` line in `/etc/default/docker` file, then restart Docker daemon with `service docker restart`.
   213  
   214  [Problem description and possible solutions](https://development.robinwinslow.uk/2016/06/23/fix-docker-networking-dns/)
   215  
   216  ## npm
   217  
   218  `npm install` (or `npm i`) no longer installs all dependencies with versions stated in `package-lock.json`, but will also attempt to load latest versions for all root packages. 
   219  If you just want to install the package dependencies without altering your `package-lock.json`, run `npm ci` instead.
   220  
   221  ## Repairing database from backups
   222  
   223  Given a current database `qf.db` and a backup `bak.db`, and we want to replace records in a table `users` of the `qf.db` with entries from the same table in `bak.db`.
   224  The database you open first will be under the alias `main`.
   225  
   226  ```sql
   227  sqlite3 qf.db
   228  delete from users;
   229  attach database '/full/path/bak.db' as backup;
   230  insert into main.users select * from backup.users;
   231  detach database backup;
   232  ```