Skip to content

Commit 7353a23

Browse files
committed
Windows CI support
1 parent 2b8635b commit 7353a23

File tree

6 files changed

+194
-92
lines changed

6 files changed

+194
-92
lines changed

.github/workflows/test.yml

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,33 @@ jobs:
1212
matrix:
1313
os:
1414
- ubuntu-latest
15-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", pypy3.9, pypy3.10]
15+
- windows-latest
16+
python-version:
17+
["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", pypy3.9, pypy3.10]
18+
exclude:
19+
# Exclude PyPy on Windows due to common compatibility issues
20+
- os: windows-latest
21+
python-version: pypy3.9
22+
- os: windows-latest
23+
python-version: pypy3.10
24+
# Exclude Python 3.13 on Windows until stable
25+
- os: windows-latest
26+
python-version: "3.13"
1627

1728
steps:
18-
- uses: actions/checkout@v4
19-
20-
- name: Set up Python ${{ matrix.python-version }}
21-
uses: actions/setup-python@v5
22-
with:
23-
python-version: ${{ matrix.python-version }}
24-
allow-prereleases: true
25-
26-
- name: Upgrade pip
27-
run: python -m pip install --upgrade pip
28-
29-
- name: Install dependencies
30-
run: pip install tox tox-gh-actions
31-
32-
- name: Test with tox
33-
run: tox
29+
- uses: actions/checkout@v4
30+
31+
- name: Set up Python ${{ matrix.python-version }}
32+
uses: actions/setup-python@v5
33+
with:
34+
python-version: ${{ matrix.python-version }}
35+
allow-prereleases: true
36+
37+
- name: Upgrade pip
38+
run: python -m pip install --upgrade pip
39+
40+
- name: Install dependencies
41+
run: pip install tox tox-gh-actions
42+
43+
- name: Test with tox
44+
run: tox

tests/test_cli.py

Lines changed: 67 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import os
2-
import sh
32
from pathlib import Path
43
from typing import Optional
54

@@ -9,43 +8,51 @@
98
from dotenv.cli import cli as dotenv_cli
109
from dotenv.version import __version__
1110

11+
from tests.utils import pushd, run_command, IS_WINDOWS
12+
13+
# Use appropriate command for the platform
14+
if IS_WINDOWS:
15+
printenv_cmd = ["dotenv", "run", "cmd", "/c", "echo", "%a%"]
16+
else:
17+
printenv_cmd = ["dotenv", "run", "printenv", "a"]
18+
1219

1320
@pytest.mark.parametrize(
1421
"format,content,expected",
1522
(
16-
(None, "x='a b c'", '''x=a b c\n'''),
17-
("simple", "x='a b c'", '''x=a b c\n'''),
18-
("simple", """x='"a b c"'""", '''x="a b c"\n'''),
19-
("simple", '''x="'a b c'"''', '''x='a b c'\n'''),
20-
("json", "x='a b c'", '''{\n "x": "a b c"\n}\n'''),
23+
(None, "x='a b c'", """x=a b c\n"""),
24+
("simple", "x='a b c'", """x=a b c\n"""),
25+
("simple", """x='"a b c"'""", """x="a b c"\n"""),
26+
("simple", '''x="'a b c'"''', """x='a b c'\n"""),
27+
("json", "x='a b c'", """{\n "x": "a b c"\n}\n"""),
2128
("shell", "x='a b c'", "x='a b c'\n"),
22-
("shell", """x='"a b c"'""", '''x='"a b c"'\n'''),
23-
("shell", '''x="'a b c'"''', '''x=''"'"'a b c'"'"''\n'''),
29+
("shell", """x='"a b c"'""", """x='"a b c"'\n"""),
30+
("shell", '''x="'a b c'"''', """x=''"'"'a b c'"'"''\n"""),
2431
("shell", "x='a\nb\nc'", "x='a\nb\nc'\n"),
25-
("export", "x='a b c'", '''export x='a b c'\n'''),
26-
)
32+
("export", "x='a b c'", """export x='a b c'\n"""),
33+
),
2734
)
2835
def test_list(cli, dotenv_path, format: Optional[str], content: str, expected: str):
29-
dotenv_path.write_text(content + '\n')
36+
dotenv_path.write_text(content + "\n")
3037

31-
args = ['--file', dotenv_path, 'list']
38+
args = ["--file", dotenv_path, "list"]
3239
if format is not None:
33-
args.extend(['--format', format])
40+
args.extend(["--format", format])
3441

3542
result = cli.invoke(dotenv_cli, args)
3643

3744
assert (result.exit_code, result.output) == (0, expected)
3845

3946

4047
def test_list_non_existent_file(cli):
41-
result = cli.invoke(dotenv_cli, ['--file', 'nx_file', 'list'])
48+
result = cli.invoke(dotenv_cli, ["--file", "nx_file", "list"])
4249

4350
assert result.exit_code == 2, result.output
4451
assert "Error opening env file" in result.output
4552

4653

4754
def test_list_not_a_file(cli):
48-
result = cli.invoke(dotenv_cli, ['--file', '.', 'list'])
55+
result = cli.invoke(dotenv_cli, ["--file", ".", "list"])
4956

5057
assert result.exit_code == 2, result.output
5158
assert "Error opening env file" in result.output
@@ -60,26 +67,26 @@ def test_list_no_file(cli):
6067
def test_get_existing_value(cli, dotenv_path):
6168
dotenv_path.write_text("a=b")
6269

63-
result = cli.invoke(dotenv_cli, ['--file', dotenv_path, 'get', 'a'])
70+
result = cli.invoke(dotenv_cli, ["--file", dotenv_path, "get", "a"])
6471

6572
assert (result.exit_code, result.output) == (0, "b\n")
6673

6774

6875
def test_get_non_existent_value(cli, dotenv_path):
69-
result = cli.invoke(dotenv_cli, ['--file', dotenv_path, 'get', 'a'])
76+
result = cli.invoke(dotenv_cli, ["--file", dotenv_path, "get", "a"])
7077

7178
assert (result.exit_code, result.output) == (1, "")
7279

7380

7481
def test_get_non_existent_file(cli):
75-
result = cli.invoke(dotenv_cli, ['--file', 'nx_file', 'get', 'a'])
82+
result = cli.invoke(dotenv_cli, ["--file", "nx_file", "get", "a"])
7683

7784
assert result.exit_code == 2
7885
assert "Error opening env file" in result.output
7986

8087

8188
def test_get_not_a_file(cli):
82-
result = cli.invoke(dotenv_cli, ['--file', '.', 'get', 'a'])
89+
result = cli.invoke(dotenv_cli, ["--file", ".", "get", "a"])
8390

8491
assert result.exit_code == 2
8592
assert "Error opening env file" in result.output
@@ -88,14 +95,14 @@ def test_get_not_a_file(cli):
8895
def test_unset_existing_value(cli, dotenv_path):
8996
dotenv_path.write_text("a=b")
9097

91-
result = cli.invoke(dotenv_cli, ['--file', dotenv_path, 'unset', 'a'])
98+
result = cli.invoke(dotenv_cli, ["--file", dotenv_path, "unset", "a"])
9299

93100
assert (result.exit_code, result.output) == (0, "Successfully removed a\n")
94101
assert dotenv_path.read_text() == ""
95102

96103

97104
def test_unset_non_existent_value(cli, dotenv_path):
98-
result = cli.invoke(dotenv_cli, ['--file', dotenv_path, 'unset', 'a'])
105+
result = cli.invoke(dotenv_cli, ["--file", dotenv_path, "unset", "a"])
99106

100107
assert (result.exit_code, result.output) == (1, "")
101108
assert dotenv_path.read_text() == ""
@@ -105,16 +112,26 @@ def test_unset_non_existent_value(cli, dotenv_path):
105112
"quote_mode,variable,value,expected",
106113
(
107114
("always", "a", "x", "a='x'\n"),
108-
("never", "a", "x", 'a=x\n'),
115+
("never", "a", "x", "a=x\n"),
109116
("auto", "a", "x", "a=x\n"),
110117
("auto", "a", "x y", "a='x y'\n"),
111118
("auto", "a", "$", "a='$'\n"),
112-
)
119+
),
113120
)
114121
def test_set_quote_options(cli, dotenv_path, quote_mode, variable, value, expected):
115122
result = cli.invoke(
116123
dotenv_cli,
117-
["--file", dotenv_path, "--export", "false", "--quote", quote_mode, "set", variable, value]
124+
[
125+
"--file",
126+
dotenv_path,
127+
"--export",
128+
"false",
129+
"--quote",
130+
quote_mode,
131+
"set",
132+
variable,
133+
value,
134+
],
118135
)
119136

120137
assert (result.exit_code, result.output) == (0, "{}={}\n".format(variable, value))
@@ -126,12 +143,22 @@ def test_set_quote_options(cli, dotenv_path, quote_mode, variable, value, expect
126143
(
127144
(Path(".nx_file"), "true", "a", "x", "export a='x'\n"),
128145
(Path(".nx_file"), "false", "a", "x", "a='x'\n"),
129-
)
146+
),
130147
)
131148
def test_set_export(cli, dotenv_path, export_mode, variable, value, expected):
132149
result = cli.invoke(
133150
dotenv_cli,
134-
["--file", dotenv_path, "--quote", "always", "--export", export_mode, "set", variable, value]
151+
[
152+
"--file",
153+
dotenv_path,
154+
"--quote",
155+
"always",
156+
"--export",
157+
export_mode,
158+
"set",
159+
variable,
160+
value,
161+
],
135162
)
136163

137164
assert (result.exit_code, result.output) == (0, "{}={}\n".format(variable, value))
@@ -152,78 +179,76 @@ def test_set_no_file(cli):
152179

153180

154181
def test_get_default_path(tmp_path):
155-
with sh.pushd(tmp_path):
182+
with pushd(tmp_path):
156183
(tmp_path / ".env").write_text("a=b")
157184

158-
result = sh.dotenv("get", "a")
185+
result = run_command(["dotenv", "get", "a"])
159186

160187
assert result == "b\n"
161188

162189

163190
def test_run(tmp_path):
164-
with sh.pushd(tmp_path):
191+
with pushd(tmp_path):
165192
(tmp_path / ".env").write_text("a=b")
166-
167-
result = sh.dotenv("run", "printenv", "a")
168-
193+
result = run_command(printenv_cmd)
169194
assert result == "b\n"
170195

171196

172197
def test_run_with_existing_variable(tmp_path):
173-
with sh.pushd(tmp_path):
198+
with pushd(tmp_path):
174199
(tmp_path / ".env").write_text("a=b")
175200
env = dict(os.environ)
176201
env.update({"LANG": "en_US.UTF-8", "a": "c"})
177202

178-
result = sh.dotenv("run", "printenv", "a", _env=env)
203+
result = run_command(printenv_cmd, env=env)
179204

180205
assert result == "b\n"
181206

182207

183208
def test_run_with_existing_variable_not_overridden(tmp_path):
184-
with sh.pushd(tmp_path):
209+
with pushd(tmp_path):
185210
(tmp_path / ".env").write_text("a=b")
186211
env = dict(os.environ)
187212
env.update({"LANG": "en_US.UTF-8", "a": "c"})
188213

189-
result = sh.dotenv("run", "--no-override", "printenv", "a", _env=env)
214+
result = run_command(printenv_cmd, env=env)
190215

191216
assert result == "c\n"
192217

193218

194219
def test_run_with_none_value(tmp_path):
195-
with sh.pushd(tmp_path):
220+
with pushd(tmp_path):
196221
(tmp_path / ".env").write_text("a=b\nc")
197222

198-
result = sh.dotenv("run", "printenv", "a")
223+
result = run_command(printenv_cmd, "a")
199224

200225
assert result == "b\n"
201226

202227

203228
def test_run_with_other_env(dotenv_path):
204229
dotenv_path.write_text("a=b")
205230

206-
result = sh.dotenv("--file", dotenv_path, "run", "printenv", "a")
231+
result = run_command(["dotenv", "--file", dotenv_path, "run", "printenv", "a"])
207232

208233
assert result == "b\n"
209234

210235

211236
def test_run_without_cmd(cli):
212-
result = cli.invoke(dotenv_cli, ['run'])
237+
result = cli.invoke(dotenv_cli, ["run"])
213238

214239
assert result.exit_code == 2
215240
assert "Invalid value for '-f'" in result.output
216241

217242

218243
def test_run_with_invalid_cmd(cli):
219-
result = cli.invoke(dotenv_cli, ['run', 'i_do_not_exist'])
244+
result = cli.invoke(dotenv_cli, ["run", "i_do_not_exist"])
220245

221246
assert result.exit_code == 2
222247
assert "Invalid value for '-f'" in result.output
223248

224249

225250
def test_run_with_version(cli):
226-
result = cli.invoke(dotenv_cli, ['--version'])
251+
result = cli.invoke(dotenv_cli, ["--version"])
227252

228253
assert result.exit_code == 0
229254
assert result.output.strip().endswith(__version__)

0 commit comments

Comments
 (0)