Skip to content

Commit 8c743c7

Browse files
committed
Implement libgit2 wasm http(s) transport layer
1 parent c8109c6 commit 8c743c7

29 files changed

+1132
-19
lines changed

.github/workflows/test-wasm.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,7 @@ jobs:
4848
- name: Run WebAssembly tests
4949
shell: bash -l {0}
5050
working-directory: wasm
51+
env:
52+
GIT2CPP_TEST_PRIVATE_TOKEN: ${{ secrets.GIT2CPP_TEST_PRIVATE_TOKEN }}
5153
run: |
5254
make test

CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ set(GIT2CPP_SRC
9898
${GIT2CPP_SOURCE_DIR}/utils/progress.hpp
9999
${GIT2CPP_SOURCE_DIR}/utils/terminal_pager.cpp
100100
${GIT2CPP_SOURCE_DIR}/utils/terminal_pager.hpp
101+
${GIT2CPP_SOURCE_DIR}/wasm/libgit2_internals.cpp
102+
${GIT2CPP_SOURCE_DIR}/wasm/libgit2_internals.hpp
103+
${GIT2CPP_SOURCE_DIR}/wasm/response.cpp
104+
${GIT2CPP_SOURCE_DIR}/wasm/response.hpp
105+
${GIT2CPP_SOURCE_DIR}/wasm/scope.cpp
106+
${GIT2CPP_SOURCE_DIR}/wasm/scope.hpp
107+
${GIT2CPP_SOURCE_DIR}/wasm/stream.cpp
108+
${GIT2CPP_SOURCE_DIR}/wasm/stream.hpp
109+
${GIT2CPP_SOURCE_DIR}/wasm/subtransport.cpp
110+
${GIT2CPP_SOURCE_DIR}/wasm/subtransport.hpp
111+
${GIT2CPP_SOURCE_DIR}/wasm/transport.cpp
112+
${GIT2CPP_SOURCE_DIR}/wasm/transport.hpp
101113
${GIT2CPP_SOURCE_DIR}/wrapper/annotated_commit_wrapper.cpp
102114
${GIT2CPP_SOURCE_DIR}/wrapper/annotated_commit_wrapper.hpp
103115
${GIT2CPP_SOURCE_DIR}/wrapper/branch_wrapper.cpp

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ used on any POSIX-compliant system.
1111

1212
See `overview.md` for further details.
1313

14+
## Development workflow
15+
16+
### Build
17+
1418
Developer's workflow using `micromamba` to manage the dependencies:
1519

1620
```bash
@@ -23,19 +27,31 @@ make -j8
2327

2428
The `git2cpp` executable can then be run, e.g. `./git2cpp -v`.
2529

30+
### Test
31+
2632
The CLI is tested using `python`. From the top-level directory:
2733

2834
```bash
2935
pytest -v
3036
```
3137

38+
Some tests access the private repository at https://github.com/QuantStack/git2cpp-test-private using
39+
a fine-grained github Personal Access Token (PAT). These tests are skipped by default. To run them
40+
you will need to obtain the PAT from one of the maintainers, and run the tests as follows:
41+
42+
```bash
43+
GIT2CPP_TEST_PRIVATE_TOKEN=<this-is-the-personal-access-token> pytest -v
44+
```
45+
46+
### pre-commit
47+
3248
`pre-commit` runs automatically on `git commit`. To run it manually use:
3349

3450
```bash
3551
pre-commit run --all-files
3652
```
3753

38-
# WebAssembly build and deployment
54+
## WebAssembly build and deployment
3955

4056
The `wasm` directory contains everything needed to build the local `git2cpp` source code as an
4157
WebAssembly [Emscripten-forge](https://emscripten-forge.org/) package, create local
@@ -48,7 +64,7 @@ See the `README.md` in the `wasm` directory for further details.
4864
The latest `cockle` and JupyterLite `terminal` deployments using `git2cpp` are available at
4965
[https://quantstack.net/git2cpp](https://quantstack.net/git2cpp)
5066

51-
# Documentation
67+
## Documentation
5268

5369
The project documentation is generated from the `git2cpp` help pages. To build the documentation
5470
locally first build `git2cpp` as usual as described above, then install the documentation

src/subcommand/clone_subcommand.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "../utils/credentials.hpp"
66
#include "../utils/input_output.hpp"
77
#include "../utils/progress.hpp"
8+
#include "../wasm/scope.hpp"
89
#include "../wrapper/repository_wrapper.hpp"
910

1011
clone_subcommand::clone_subcommand(const libgit2_object&, CLI::App& app)
@@ -29,6 +30,8 @@ clone_subcommand::clone_subcommand(const libgit2_object&, CLI::App& app)
2930

3031
void clone_subcommand::run()
3132
{
33+
wasm_http_transport_scope transport; // Enables wasm http(s) transport.
34+
3235
// m_depth = 0 means no shallow clone in libgit2, while
3336
// it is forbidden with git. Therefore we use another
3437
// sentinel value to detect full clone.

src/subcommand/fetch_subcommand.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "../utils/credentials.hpp"
88
#include "../utils/input_output.hpp"
99
#include "../utils/progress.hpp"
10+
#include "../wasm/scope.hpp"
1011
#include "../wrapper/repository_wrapper.hpp"
1112

1213
fetch_subcommand::fetch_subcommand(const libgit2_object&, CLI::App& app)
@@ -32,6 +33,8 @@ fetch_subcommand::fetch_subcommand(const libgit2_object&, CLI::App& app)
3233

3334
void fetch_subcommand::run()
3435
{
36+
wasm_http_transport_scope transport; // Enables wasm http(s) transport.
37+
3538
auto directory = get_current_git_path();
3639
auto repo = repository_wrapper::open(directory);
3740

src/subcommand/push_subcommand.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "../utils/credentials.hpp"
88
#include "../utils/progress.hpp"
9+
#include "../wasm/scope.hpp"
910
#include "../wrapper/repository_wrapper.hpp"
1011

1112
push_subcommand::push_subcommand(const libgit2_object&, CLI::App& app)
@@ -26,6 +27,8 @@ push_subcommand::push_subcommand(const libgit2_object&, CLI::App& app)
2627

2728
void push_subcommand::run()
2829
{
30+
wasm_http_transport_scope transport; // Enables wasm http(s) transport.
31+
2932
auto directory = get_current_git_path();
3033
auto repo = repository_wrapper::open(directory);
3134

src/utils/common.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <iostream>
66
#include <map>
77
#include <ranges>
8+
#include <regex>
89
#include <sstream>
910

1011
#include <git2.h>
@@ -135,3 +136,9 @@ std::vector<std::string> split_input_at_newlines(std::string_view str)
135136
);
136137
return std::vector<std::string>{split.begin(), split.end()};
137138
}
139+
140+
std::string trim(const std::string& str)
141+
{
142+
auto s = std::regex_replace(str, std::regex("^\\s+"), "");
143+
return std::regex_replace(s, std::regex("\\s+$"), "");
144+
}

src/utils/common.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,6 @@ class git_strarray_wrapper
7676
std::string read_file(const std::string& path);
7777

7878
std::vector<std::string> split_input_at_newlines(std::string_view str);
79+
80+
// Remove whitespace from start and end of a string.
81+
std::string trim(const std::string& str);

src/utils/credentials.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ int user_credentials(
2525
std::string username = username_from_url ? username_from_url : "";
2626
if (username.empty())
2727
{
28-
username = prompt_input("Username: ");
28+
username = trim(prompt_input("Username: "));
2929
}
3030
if (username.empty())
3131
{
3232
giterr_set_str(GIT_ERROR_HTTP, "No username specified");
3333
return GIT_EAUTH;
3434
}
3535

36-
std::string password = prompt_input("Password: ", false);
36+
std::string password = trim(prompt_input("Password: ", false));
3737
if (password.empty())
3838
{
3939
giterr_set_str(GIT_ERROR_HTTP, "No password specified");

src/wasm/libgit2_internals.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#ifdef EMSCRIPTEN
2+
3+
# include "libgit2_internals.hpp"
4+
5+
// http method and service.
6+
7+
std::string name_for_method(git_http_method method)
8+
{
9+
switch (method)
10+
{
11+
case GIT_HTTP_METHOD_GET:
12+
return "GET";
13+
case GIT_HTTP_METHOD_POST:
14+
return "POST";
15+
case GIT_HTTP_METHOD_CONNECT:
16+
return "CONNECT";
17+
}
18+
return "";
19+
}
20+
21+
std::optional<http_service> select_service(git_smart_service_t action)
22+
{
23+
switch (action)
24+
{
25+
case GIT_SERVICE_UPLOADPACK_LS:
26+
return http_service{
27+
GIT_HTTP_METHOD_GET,
28+
"/info/refs?service=git-upload-pack",
29+
nullptr,
30+
"application/x-git-upload-pack-advertisement",
31+
1,
32+
0
33+
};
34+
case GIT_SERVICE_UPLOADPACK:
35+
return http_service{
36+
GIT_HTTP_METHOD_POST,
37+
"/git-upload-pack",
38+
"application/x-git-upload-pack-request",
39+
"application/x-git-upload-pack-result",
40+
0,
41+
0
42+
};
43+
case GIT_SERVICE_RECEIVEPACK_LS:
44+
return http_service{
45+
GIT_HTTP_METHOD_GET,
46+
"/info/refs?service=git-receive-pack",
47+
nullptr,
48+
"application/x-git-receive-pack-advertisement",
49+
1,
50+
0
51+
};
52+
case GIT_SERVICE_RECEIVEPACK:
53+
return http_service{
54+
GIT_HTTP_METHOD_POST,
55+
"/git-receive-pack",
56+
"application/x-git-receive-pack-request",
57+
"application/x-git-receive-pack-result",
58+
0,
59+
1
60+
};
61+
}
62+
63+
return std::nullopt;
64+
}
65+
66+
#endif // EMSCRIPTEN

0 commit comments

Comments
 (0)