Skip to content

Commit 770534f

Browse files
committed
Merge pull request #371 from dscho/run-scalar-functional-tests-and-fix-built-in-fsmonitor
Fix the built-in FSMonitor, and run Scalar's Functional Tests as part of the automated builds
2 parents e96a876 + 0ba21c3 commit 770534f

21 files changed

+1700
-43
lines changed
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
name: Scalar Functional Tests
2+
3+
env:
4+
SCALAR_REPOSITORY: microsoft/scalar
5+
SCALAR_REF: main
6+
DEBUG_WITH_TMATE: false
7+
SCALAR_TEST_SKIP_VSTS_INFO: true
8+
9+
on:
10+
push:
11+
branches: [ vfs-*, tentative/vfs-* ]
12+
pull_request:
13+
branches: [ vfs-*, features/* ]
14+
15+
jobs:
16+
scalar:
17+
name: "Scalar Functional Tests"
18+
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
# Order by runtime (in descending order)
23+
os: [windows-2019, macos-11, ubuntu-20.04, ubuntu-22.04]
24+
# Scalar.NET used to be tested using `features: [false, experimental]`
25+
# But currently, Scalar/C ignores `feature.scalar` altogether, so let's
26+
# save some electrons and run only one of them...
27+
features: [ignored]
28+
exclude:
29+
# The built-in FSMonitor is not (yet) supported on Linux
30+
- os: ubuntu-20.04
31+
features: experimental
32+
- os: ubuntu-22.04
33+
features: experimental
34+
runs-on: ${{ matrix.os }}
35+
36+
env:
37+
BUILD_FRAGMENT: bin/Release/netcoreapp3.1
38+
GIT_FORCE_UNTRACKED_CACHE: 1
39+
40+
steps:
41+
- name: Check out Git's source code
42+
uses: actions/checkout@v3
43+
44+
- name: Setup build tools on Windows
45+
if: runner.os == 'Windows'
46+
uses: git-for-windows/setup-git-for-windows-sdk@v1
47+
48+
- name: Provide a minimal `install` on Windows
49+
if: runner.os == 'Windows'
50+
shell: bash
51+
run: |
52+
test -x /usr/bin/install ||
53+
tr % '\t' >/usr/bin/install <<-\EOF
54+
#!/bin/sh
55+
56+
cmd=cp
57+
while test $# != 0
58+
do
59+
%case "$1" in
60+
%-d) cmd="mkdir -p";;
61+
%-m) shift;; # ignore mode
62+
%*) break;;
63+
%esac
64+
%shift
65+
done
66+
67+
exec $cmd "$@"
68+
EOF
69+
70+
- name: Install build dependencies for Git (Linux)
71+
if: runner.os == 'Linux'
72+
run: |
73+
sudo apt-get update
74+
sudo apt-get -q -y install libssl-dev libcurl4-openssl-dev gettext
75+
76+
- name: Build and install Git
77+
shell: bash
78+
env:
79+
NO_TCLTK: Yup
80+
run: |
81+
# We do require a VFS version
82+
def_ver="$(sed -n 's/DEF_VER=\(.*vfs.*\)/\1/p' GIT-VERSION-GEN)"
83+
test -n "$def_ver"
84+
85+
# Ensure that `git version` reflects DEF_VER
86+
case "$(git describe --match "v[0-9]*vfs*" HEAD)" in
87+
${def_ver%%.vfs.*}.vfs.*) ;; # okay, we can use this
88+
*) git -c user.name=ci -c user.email=ci@github tag -m for-testing ${def_ver}.NNN.g$(git rev-parse --short HEAD);;
89+
esac
90+
91+
SUDO=
92+
extra=
93+
case "${{ runner.os }}" in
94+
Windows)
95+
extra=DESTDIR=/c/Progra~1/Git
96+
cygpath -aw "/c/Program Files/Git/cmd" >>$GITHUB_PATH
97+
;;
98+
Linux)
99+
SUDO=sudo
100+
extra=prefix=/usr
101+
;;
102+
macOS)
103+
SUDO=sudo
104+
extra=prefix=/usr/local
105+
;;
106+
esac
107+
108+
$SUDO make -j5 $extra install
109+
110+
- name: Ensure that we use the built Git and Scalar
111+
shell: bash
112+
run: |
113+
type -p git
114+
git version
115+
case "$(git version)" in *.vfs.*) echo Good;; *) exit 1;; esac
116+
type -p scalar
117+
scalar version
118+
case "$(scalar version 2>&1)" in *.vfs.*) echo Good;; *) exit 1;; esac
119+
120+
- name: Check out Scalar's source code
121+
uses: actions/checkout@v3
122+
with:
123+
fetch-depth: 0 # Indicate full history so Nerdbank.GitVersioning works.
124+
path: scalar
125+
repository: ${{ env.SCALAR_REPOSITORY }}
126+
ref: ${{ env.SCALAR_REF }}
127+
128+
- name: Setup .NET Core
129+
uses: actions/setup-dotnet@v3
130+
with:
131+
dotnet-version: '3.1.x'
132+
133+
- name: Install dependencies
134+
run: dotnet restore
135+
working-directory: scalar
136+
env:
137+
DOTNET_NOLOGO: 1
138+
139+
- name: Build
140+
working-directory: scalar
141+
run: dotnet build --configuration Release --no-restore -p:UseAppHost=true # Force generation of executable on macOS.
142+
143+
- name: Setup platform (Linux)
144+
if: runner.os == 'Linux'
145+
run: |
146+
echo "BUILD_PLATFORM=${{ runner.os }}" >>$GITHUB_ENV
147+
echo "TRACE2_BASENAME=Trace2.${{ github.run_id }}__${{ github.run_number }}__${{ matrix.os }}__${{ matrix.features }}" >>$GITHUB_ENV
148+
149+
- name: Setup platform (Mac)
150+
if: runner.os == 'macOS'
151+
run: |
152+
echo 'BUILD_PLATFORM=Mac' >>$GITHUB_ENV
153+
echo "TRACE2_BASENAME=Trace2.${{ github.run_id }}__${{ github.run_number }}__${{ matrix.os }}__${{ matrix.features }}" >>$GITHUB_ENV
154+
155+
- name: Setup platform (Windows)
156+
if: runner.os == 'Windows'
157+
run: |
158+
echo "BUILD_PLATFORM=${{ runner.os }}" >>$env:GITHUB_ENV
159+
echo 'BUILD_FILE_EXT=.exe' >>$env:GITHUB_ENV
160+
echo "TRACE2_BASENAME=Trace2.${{ github.run_id }}__${{ github.run_number }}__${{ matrix.os }}__${{ matrix.features }}" >>$env:GITHUB_ENV
161+
162+
- name: Configure feature.scalar
163+
run: git config --global feature.scalar ${{ matrix.features }}
164+
165+
- id: functional_test
166+
name: Functional test
167+
timeout-minutes: 60
168+
working-directory: scalar
169+
shell: bash
170+
run: |
171+
export GIT_TRACE2_EVENT="$PWD/$TRACE2_BASENAME/Event"
172+
export GIT_TRACE2_PERF="$PWD/$TRACE2_BASENAME/Perf"
173+
export GIT_TRACE2_EVENT_BRIEF=true
174+
export GIT_TRACE2_PERF_BRIEF=true
175+
mkdir -p "$TRACE2_BASENAME"
176+
mkdir -p "$TRACE2_BASENAME/Event"
177+
mkdir -p "$TRACE2_BASENAME/Perf"
178+
git version --build-options
179+
cd ../out
180+
Scalar.FunctionalTests/$BUILD_FRAGMENT/Scalar.FunctionalTests$BUILD_FILE_EXT --test-scalar-on-path --test-git-on-path --timeout=300000 --full-suite
181+
182+
- name: Force-stop FSMonitor daemons and Git processes (Windows)
183+
if: runner.os == 'Windows' && (success() || failure())
184+
shell: bash
185+
run: |
186+
set -x
187+
wmic process get CommandLine,ExecutablePath,HandleCount,Name,ParentProcessID,ProcessID
188+
wmic process where "CommandLine Like '%fsmonitor--daemon %run'" delete
189+
wmic process where "ExecutablePath Like '%git.exe'" delete
190+
191+
- id: trace2_zip_unix
192+
if: runner.os != 'Windows' && ( success() || failure() ) && ( steps.functional_test.conclusion == 'success' || steps.functional_test.conclusion == 'failure' )
193+
name: Zip Trace2 Logs (Unix)
194+
shell: bash
195+
working-directory: scalar
196+
run: zip -q -r $TRACE2_BASENAME.zip $TRACE2_BASENAME/
197+
198+
- id: trace2_zip_windows
199+
if: runner.os == 'Windows' && ( success() || failure() ) && ( steps.functional_test.conclusion == 'success' || steps.functional_test.conclusion == 'failure' )
200+
name: Zip Trace2 Logs (Windows)
201+
working-directory: scalar
202+
run: Compress-Archive -DestinationPath ${{ env.TRACE2_BASENAME }}.zip -Path ${{ env.TRACE2_BASENAME }}
203+
204+
- name: Archive Trace2 Logs
205+
if: ( success() || failure() ) && ( steps.trace2_zip_unix.conclusion == 'success' || steps.trace2_zip_windows.conclusion == 'success' )
206+
uses: actions/upload-artifact@v3
207+
with:
208+
name: ${{ env.TRACE2_BASENAME }}.zip
209+
path: scalar/${{ env.TRACE2_BASENAME }}.zip
210+
retention-days: 3
211+
212+
# The GitHub Action `action-tmate` allows developers to connect to the running agent
213+
# using SSH (it will be a `tmux` session; on Windows agents it will be inside the MSYS2
214+
# environment in `C:\msys64`, therefore it can be slightly tricky to interact with
215+
# Git for Windows, which runs a slightly incompatible MSYS2 runtime).
216+
- name: action-tmate
217+
if: env.DEBUG_WITH_TMATE == 'true' && failure()
218+
uses: mxschmitt/action-tmate@v3
219+
with:
220+
limit-access-to-actor: true

Documentation/config/core.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,3 +833,12 @@ core.WSLCompat::
833833
The default value is false. When set to true, Git will set the mode
834834
bits of the file in the way of wsl, so that the executable flag of
835835
files can be set or read correctly.
836+
837+
core.configWriteLockTimeoutMS::
838+
When processes try to write to the config concurrently, it is likely
839+
that one process "wins" and the other process(es) fail to lock the
840+
config file. By configuring a timeout larger than zero, Git can be
841+
told to try to lock the config again a couple times within the
842+
specified timeout. If the timeout is configure to zero (which is the
843+
default), Git will fail immediately when the config is already
844+
locked.

Documentation/scalar.txt

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ SYNOPSIS
99
--------
1010
[verse]
1111
scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]
12-
[--[no-]src] <url> [<enlistment>]
12+
[--[no-]src] [--local-cache-path <path>] [--cache-server-url <url>]
13+
<url> [<enlistment>]
1314
scalar list
1415
scalar register [<enlistment>]
1516
scalar unregister [<enlistment>]
1617
scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]
1718
scalar reconfigure [ --all | <enlistment> ]
1819
scalar diagnose [<enlistment>]
1920
scalar delete <enlistment>
21+
scalar cache-server ( --get | --set <url> | --list [<remote>] ) [<enlistment>]
2022

2123
DESCRIPTION
2224
-----------
@@ -90,6 +92,17 @@ cloning. If the HEAD at the remote did not point at any branch when
9092
A sparse-checkout is initialized by default. This behavior can be
9193
turned off via `--full-clone`.
9294

95+
--local-cache-path <path>::
96+
Override the path to the local cache root directory; Pre-fetched objects
97+
are stored into a repository-dependent subdirectory of that path.
98+
+
99+
The default is `<drive>:\.scalarCache` on Windows (on the same drive as the
100+
clone), and `~/.scalarCache` on macOS.
101+
102+
--cache-server-url <url>::
103+
Retrieve missing objects from the specified remote, which is expected to
104+
understand the GVFS protocol.
105+
93106
List
94107
~~~~
95108

@@ -163,6 +176,27 @@ delete <enlistment>::
163176
This subcommand lets you delete an existing Scalar enlistment from your
164177
local file system, unregistering the repository.
165178

179+
Cache-server
180+
~~~~~~~~~~~~
181+
182+
cache-server ( --get | --set <url> | --list [<remote>] ) [<enlistment>]::
183+
This command lets you query or set the GVFS-enabled cache server used
184+
to fetch missing objects.
185+
186+
--get::
187+
This is the default command mode: query the currently-configured cache
188+
server URL, if any.
189+
190+
--list::
191+
Access the `gvfs/info` endpoint of the specified remote (default:
192+
`origin`) to figure out which cache servers are available, if any.
193+
+
194+
In contrast to the `--get` command mode (which only accesses the local
195+
repository), this command mode triggers a request via the network that
196+
potentially requires authentication. If authentication is required, the
197+
configured credential helper is employed (see linkgit:git-credential[1]
198+
for details).
199+
166200
SEE ALSO
167201
--------
168202
linkgit:git-clone[1], linkgit:git-maintenance[1].

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2763,6 +2763,7 @@ GIT_OBJS += git.o
27632763
.PHONY: git-objs
27642764
git-objs: $(GIT_OBJS)
27652765

2766+
SCALAR_OBJS := json-parser.o
27662767
SCALAR_OBJS += scalar.o
27672768
.PHONY: scalar-objs
27682769
scalar-objs: $(SCALAR_OBJS)
@@ -2912,7 +2913,7 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o $(LAZYLOAD_LIBCURL_OB
29122913
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
29132914
$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
29142915

2915-
scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS)
2916+
scalar$X: $(SCALAR_OBJS) GIT-LDFLAGS $(GITLIBS)
29162917
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
29172918
$(filter %.o,$^) $(LIBS)
29182919

abspath.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ int is_directory(const char *path)
1414
}
1515

1616
/* removes the last path component from 'path' except if 'path' is root */
17-
static void strip_last_component(struct strbuf *path)
17+
void strip_last_path_component(struct strbuf *path)
1818
{
1919
size_t offset = offset_1st_component(path->buf);
2020
size_t len = path->len;
@@ -119,7 +119,7 @@ static char *strbuf_realpath_1(struct strbuf *resolved, const char *path,
119119
continue; /* '.' component */
120120
} else if (next.len == 2 && !strcmp(next.buf, "..")) {
121121
/* '..' component; strip the last path component */
122-
strip_last_component(resolved);
122+
strip_last_path_component(resolved);
123123
continue;
124124
}
125125

@@ -171,7 +171,7 @@ static char *strbuf_realpath_1(struct strbuf *resolved, const char *path,
171171
* strip off the last component since it will
172172
* be replaced with the contents of the symlink
173173
*/
174-
strip_last_component(resolved);
174+
strip_last_path_component(resolved);
175175
}
176176

177177
/*

abspath.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ char *real_pathdup(const char *path, int die_on_error);
1010
const char *absolute_path(const char *path);
1111
char *absolute_pathdup(const char *path);
1212

13+
/**
14+
* Remove the last path component from 'path' except if 'path' is root.
15+
*/
16+
void strip_last_path_component(struct strbuf *path);
17+
1318
/*
1419
* Concatenate "prefix" (if len is non-zero) and "path", with no
1520
* connecting characters (so "prefix" should end with a "/").

config.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3371,6 +3371,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
33713371
const char *comment,
33723372
unsigned flags)
33733373
{
3374+
static unsigned long timeout_ms = ULONG_MAX;
33743375
int fd = -1, in_fd = -1;
33753376
int ret;
33763377
struct lock_file lock = LOCK_INIT;
@@ -3391,11 +3392,16 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
33913392
if (!config_filename)
33923393
config_filename = filename_buf = git_pathdup("config");
33933394

3395+
if ((long)timeout_ms < 0 &&
3396+
git_config_get_ulong("core.configWriteLockTimeoutMS", &timeout_ms))
3397+
timeout_ms = 0;
3398+
33943399
/*
33953400
* The lock serves a purpose in addition to locking: the new
33963401
* contents of .git/config will be written into it.
33973402
*/
3398-
fd = hold_lock_file_for_update(&lock, config_filename, 0);
3403+
fd = hold_lock_file_for_update_timeout(&lock, config_filename, 0,
3404+
timeout_ms);
33993405
if (fd < 0) {
34003406
error_errno(_("could not lock config file %s"), config_filename);
34013407
ret = CONFIG_NO_LOCK;

contrib/buildsystems/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,7 @@ target_link_libraries(git-sh-i18n--envsubst common-main)
799799
add_executable(git-shell ${CMAKE_SOURCE_DIR}/shell.c)
800800
target_link_libraries(git-shell common-main)
801801

802-
add_executable(scalar ${CMAKE_SOURCE_DIR}/scalar.c)
802+
add_executable(scalar ${CMAKE_SOURCE_DIR}/scalar.c ${CMAKE_SOURCE_DIR}/json-parser.c)
803803
target_link_libraries(scalar common-main)
804804

805805
if(CURL_FOUND)

0 commit comments

Comments
 (0)