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 ```