aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG20
-rw-r--r--examples/complex/dup_and_replay.py2
-rw-r--r--mitmproxy/addonmanager.py5
-rw-r--r--mitmproxy/addons/view.py219
-rw-r--r--mitmproxy/tools/console/consoleaddons.py4
-rw-r--r--mitmproxy/tools/console/defaultkeys.py12
-rw-r--r--mitmproxy/tools/console/flowlist.py12
-rw-r--r--mitmproxy/tools/console/flowview.py2
-rw-r--r--mitmproxy/tools/console/statusbar.py4
-rw-r--r--mitmproxy/types.py4
-rw-r--r--release/README.md9
-rwxr-xr-xrelease/cibuild.py54
-rw-r--r--test/mitmproxy/addons/test_view.py50
-rw-r--r--test/mitmproxy/test_command.py2
-rw-r--r--test/mitmproxy/test_types.py2
-rw-r--r--test/release/test_cibuild.py91
16 files changed, 330 insertions, 162 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 4997ecd4..ddf1afe5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,23 @@
+15 June 2018: mitmproxy 4.0.3
+
+ * Add support for IPv6 transparent mode on Windows (#3174)
+ * Add Docker images for ARMv7 - Raspberry Pi (#3190)
+ * Major overhaul of our release workflow - you probably won't notice it, but for us it's a big thing!
+ * Fix the Python version detection on Python 3.5, we now show a more intuitive error message (#3188)
+ * Fix application shutdown on Windows (#3172)
+ * Fix IPv6 scope suffixes in block addon (#3164)
+ * Fix options update when added (#3157)
+ * Fix "Edit Flow" button in mitmweb (#3136)
+
+15 June 2018: mitmproxy 4.0.2
+ * Skipped!
+
+
+17 May 2018: mitmproxy 4.0.1
+
+ ** Bugfixes **
+ * The previous release had a packaging issue, so we bumped it to v4.0.1 and re-released it.
+ * This contains no actual bugfixes or new features.
17 May 2018: mitmproxy 4.0
diff --git a/examples/complex/dup_and_replay.py b/examples/complex/dup_and_replay.py
index adcebff3..3ad98dc5 100644
--- a/examples/complex/dup_and_replay.py
+++ b/examples/complex/dup_and_replay.py
@@ -9,6 +9,6 @@ def request(flow):
# Only interactive tools have a view. If we have one, add a duplicate entry
# for our flow.
if "view" in ctx.master.addons:
- ctx.master.commands.call("view.add", [flow])
+ ctx.master.commands.call("view.flows.add", [flow])
flow.request.path = "/changed"
ctx.master.commands.call("replay.client", [flow])
diff --git a/mitmproxy/addonmanager.py b/mitmproxy/addonmanager.py
index 0b559293..645f3a93 100644
--- a/mitmproxy/addonmanager.py
+++ b/mitmproxy/addonmanager.py
@@ -123,9 +123,10 @@ class AddonManager:
"""
Remove all addons.
"""
- for i in self.chain:
- self.remove(i)
+ for a in self.chain:
+ self.invoke_addon(a, "done")
self.lookup = {}
+ self.chain = []
def get(self, name):
"""
diff --git a/mitmproxy/addons/view.py b/mitmproxy/addons/view.py
index ae00e9d1..1c8bd0ce 100644
--- a/mitmproxy/addons/view.py
+++ b/mitmproxy/addons/view.py
@@ -166,12 +166,6 @@ class View(collections.Sequence):
def store_count(self):
return len(self._store)
- def inbounds(self, index: int) -> bool:
- """
- Is this 0 <= index < len(self)
- """
- return 0 <= index < len(self)
-
def _rev(self, idx: int) -> int:
"""
Reverses an index, if needed
@@ -219,7 +213,26 @@ class View(collections.Sequence):
self._base_add(i)
self.sig_view_refresh.send(self)
- # API
+ """ View API """
+
+ # Focus
+ @command.command("view.focus.go")
+ def go(self, dst: int) -> None:
+ """
+ Go to a specified offset. Positive offests are from the beginning of
+ the view, negative from the end of the view, so that 0 is the first
+ flow, -1 is the last flow.
+ """
+ if len(self) == 0:
+ return
+ if dst < 0:
+ dst = len(self) + dst
+ if dst < 0:
+ dst = 0
+ if dst > len(self) - 1:
+ dst = len(self) - 1
+ self.focus.flow = self[dst]
+
@command.command("view.focus.next")
def focus_next(self) -> None:
"""
@@ -238,6 +251,7 @@ class View(collections.Sequence):
if self.inbounds(idx):
self.focus.flow = self[idx]
+ # Order
@command.command("view.order.options")
def order_options(self) -> typing.Sequence[str]:
"""
@@ -245,34 +259,58 @@ class View(collections.Sequence):
"""
return list(sorted(self.orders.keys()))
- @command.command("view.marked.toggle")
- def toggle_marked(self) -> None:
- """
- Toggle whether to show marked views only.
- """
- self.show_marked = not self.show_marked
- self._refilter()
-
- def set_reversed(self, value: bool):
+ @command.command("view.order.reverse")
+ def set_reversed(self, value: bool) -> None:
self.order_reversed = value
self.sig_view_refresh.send(self)
- def set_order(self, order_key: typing.Callable):
+ @command.command("view.order.set")
+ def set_order(self, order: str) -> None:
"""
Sets the current view order.
"""
+ if order not in self.orders:
+ raise exceptions.CommandError(
+ "Unknown flow order: %s" % order
+ )
+ order_key = self.orders[order]
self.order_key = order_key
newview = sortedcontainers.SortedListWithKey(key=order_key)
newview.update(self._view)
self._view = newview
- def set_filter(self, flt: typing.Optional[flowfilter.TFilter]):
+ @command.command("view.order")
+ def get_order(self) -> str:
+ """
+ Returns the current view order.
+ """
+ order = ""
+ for k in self.orders.keys():
+ if self.order_key == self.orders[k]:
+ order = k
+ return order
+
+ # Filter
+ @command.command("view.filter.set")
+ def set_filter_cmd(self, f: str) -> None:
"""
Sets the current view filter.
"""
+ filt = None
+ if f:
+ filt = flowfilter.parse(f)
+ if not filt:
+ raise exceptions.CommandError(
+ "Invalid interception filter: %s" % f
+ )
+ self.set_filter(filt)
+
+ def set_filter(self, flt: typing.Optional[flowfilter.TFilter]):
self.filter = flt or matchall
self._refilter()
+ # View Updates
+ @command.command("view.clear")
def clear(self) -> None:
"""
Clears both the store and view.
@@ -282,7 +320,8 @@ class View(collections.Sequence):
self.sig_view_refresh.send(self)
self.sig_store_refresh.send(self)
- def clear_not_marked(self):
+ @command.command("view.clear_unmarked")
+ def clear_not_marked(self) -> None:
"""
Clears only the unmarked flows.
"""
@@ -293,36 +332,15 @@ class View(collections.Sequence):
self._refilter()
self.sig_store_refresh.send(self)
- @command.command("view.marked.toggle")
- def add(self, flows: typing.Sequence[mitmproxy.flow.Flow]) -> None:
- """
- Adds a flow to the state. If the flow already exists, it is
- ignored.
- """
- for f in flows:
- if f.id not in self._store:
- self._store[f.id] = f
- if self.filter(f):
- self._base_add(f)
- if self.focus_follow:
- self.focus.flow = f
- self.sig_view_add.send(self, flow=f)
-
- def get_by_id(self, flow_id: str) -> typing.Optional[mitmproxy.flow.Flow]:
- """
- Get flow with the given id from the store.
- Returns None if the flow is not found.
- """
- return self._store.get(flow_id)
-
- @command.command("view.getval")
+ # View Settings
+ @command.command("view.settings.getval")
def getvalue(self, f: mitmproxy.flow.Flow, key: str, default: str) -> str:
"""
Get a value from the settings store for the specified flow.
"""
return self.settings[f].get(key, default)
- @command.command("view.setval.toggle")
+ @command.command("view.settings.setval.toggle")
def setvalue_toggle(
self,
flows: typing.Sequence[mitmproxy.flow.Flow],
@@ -339,7 +357,7 @@ class View(collections.Sequence):
updated.append(f)
ctx.master.addons.trigger("update", updated)
- @command.command("view.setval")
+ @command.command("view.settings.setval")
def setvalue(
self,
flows: typing.Sequence[mitmproxy.flow.Flow],
@@ -354,41 +372,8 @@ class View(collections.Sequence):
updated.append(f)
ctx.master.addons.trigger("update", updated)
- @command.command("view.load")
- def load_file(self, path: mitmproxy.types.Path) -> None:
- """
- Load flows into the view, without processing them with addons.
- """
- try:
- with open(path, "rb") as f:
- for i in io.FlowReader(f).stream():
- # Do this to get a new ID, so we can load the same file N times and
- # get new flows each time. It would be more efficient to just have a
- # .newid() method or something.
- self.add([i.copy()])
- except IOError as e:
- ctx.log.error(e.strerror)
- except exceptions.FlowReadException as e:
- ctx.log.error(str(e))
-
- @command.command("view.go")
- def go(self, dst: int) -> None:
- """
- Go to a specified offset. Positive offests are from the beginning of
- the view, negative from the end of the view, so that 0 is the first
- flow, -1 is the last flow.
- """
- if len(self) == 0:
- return
- if dst < 0:
- dst = len(self) + dst
- if dst < 0:
- dst = 0
- if dst > len(self) - 1:
- dst = len(self) - 1
- self.focus.flow = self[dst]
-
- @command.command("view.duplicate")
+ # Flows
+ @command.command("view.flows.duplicate")
def duplicate(self, flows: typing.Sequence[mitmproxy.flow.Flow]) -> None:
"""
Duplicates the specified flows, and sets the focus to the first
@@ -400,7 +385,7 @@ class View(collections.Sequence):
self.focus.flow = dups[0]
ctx.log.alert("Duplicated %s flows" % len(dups))
- @command.command("view.remove")
+ @command.command("view.flows.remove")
def remove(self, flows: typing.Sequence[mitmproxy.flow.Flow]) -> None:
"""
Removes the flow from the underlying store and the view.
@@ -420,7 +405,7 @@ class View(collections.Sequence):
if len(flows) > 1:
ctx.log.alert("Removed %s flows" % len(flows))
- @command.command("view.resolve")
+ @command.command("view.flows.resolve")
def resolve(self, spec: str) -> typing.Sequence[mitmproxy.flow.Flow]:
"""
Resolve a flow list specification to an actual list of flows.
@@ -443,7 +428,7 @@ class View(collections.Sequence):
raise exceptions.CommandError("Invalid flow filter: %s" % spec)
return [i for i in self._store.values() if filt(i)]
- @command.command("view.create")
+ @command.command("view.flows.create")
def create(self, method: str, url: str) -> None:
try:
req = http.HTTPRequest.make(method.upper(), url)
@@ -456,6 +441,74 @@ class View(collections.Sequence):
f.request.headers["Host"] = req.host
self.add([f])
+ @command.command("view.flows.load")
+ def load_file(self, path: mitmproxy.types.Path) -> None:
+ """
+ Load flows into the view, without processing them with addons.
+ """
+ try:
+ with open(path, "rb") as f:
+ for i in io.FlowReader(f).stream():
+ # Do this to get a new ID, so we can load the same file N times and
+ # get new flows each time. It would be more efficient to just have a
+ # .newid() method or something.
+ self.add([i.copy()])
+ except IOError as e:
+ ctx.log.error(e.strerror)
+ except exceptions.FlowReadException as e:
+ ctx.log.error(str(e))
+
+ def add(self, flows: typing.Sequence[mitmproxy.flow.Flow]) -> None:
+ """
+ Adds a flow to the state. If the flow already exists, it is
+ ignored.
+ """
+ for f in flows:
+ if f.id not in self._store:
+ self._store[f.id] = f
+ if self.filter(f):
+ self._base_add(f)
+ if self.focus_follow:
+ self.focus.flow = f
+ self.sig_view_add.send(self, flow=f)
+
+ def get_by_id(self, flow_id: str) -> typing.Optional[mitmproxy.flow.Flow]:
+ """
+ Get flow with the given id from the store.
+ Returns None if the flow is not found.
+ """
+ return self._store.get(flow_id)
+
+ # View Properties
+ @command.command("view.properties.length")
+ def get_length(self) -> int:
+ """
+ Returns view length.
+ """
+ return len(self)
+
+ @command.command("view.properties.marked")
+ def get_marked(self) -> bool:
+ """
+ Returns true if view is in marked mode.
+ """
+ return self.show_marked
+
+ @command.command("view.properties.marked.toggle")
+ def toggle_marked(self) -> None:
+ """
+ Toggle whether to show marked views only.
+ """
+ self.show_marked = not self.show_marked
+ self._refilter()
+
+ @command.command("view.properties.inbounds")
+ def inbounds(self, index: int) -> bool:
+ """
+ Is this 0 <= index < len(self)?
+ """
+ return 0 <= index < len(self)
+
# Event handlers
def configure(self, updated):
if "view_filter" in updated:
@@ -472,7 +525,7 @@ class View(collections.Sequence):
raise exceptions.OptionsError(
"Unknown flow order: %s" % ctx.options.view_order
)
- self.set_order(self.orders[ctx.options.view_order])
+ self.set_order(ctx.options.view_order)
if "view_order_reversed" in updated:
self.set_reversed(ctx.options.view_order_reversed)
if "console_focus_follow" in updated:
diff --git a/mitmproxy/tools/console/consoleaddons.py b/mitmproxy/tools/console/consoleaddons.py
index 54fe11c4..a40cdeaa 100644
--- a/mitmproxy/tools/console/consoleaddons.py
+++ b/mitmproxy/tools/console/consoleaddons.py
@@ -515,7 +515,7 @@ class ConsoleAddon:
try:
self.master.commands.call_strings(
- "view.setval",
+ "view.settings.setval",
["@focus", "flowview_mode_%s" % idx, mode]
)
except exceptions.CommandError as e:
@@ -538,7 +538,7 @@ class ConsoleAddon:
raise exceptions.CommandError("Not viewing a flow.")
idx = fv.body.tab_offset
return self.master.commands.call_strings(
- "view.getval",
+ "view.settings.getval",
[
"@focus",
"flowview_mode_%s" % idx,
diff --git a/mitmproxy/tools/console/defaultkeys.py b/mitmproxy/tools/console/defaultkeys.py
index 7f65c1f7..0f2e9072 100644
--- a/mitmproxy/tools/console/defaultkeys.py
+++ b/mitmproxy/tools/console/defaultkeys.py
@@ -36,8 +36,8 @@ def map(km):
["flowlist", "flowview"],
"Save response body to file"
)
- km.add("d", "view.remove @focus", ["flowlist", "flowview"], "Delete flow from view")
- km.add("D", "view.duplicate @focus", ["flowlist", "flowview"], "Duplicate flow")
+ km.add("d", "view.flows.remove @focus", ["flowlist", "flowview"], "Delete flow from view")
+ km.add("D", "view.flows.duplicate @focus", ["flowlist", "flowview"], "Duplicate flow")
km.add(
"e",
"""
@@ -57,7 +57,7 @@ def map(km):
)
km.add("L", "console.command view.load ", ["flowlist"], "Load flows from file")
km.add("m", "flow.mark.toggle @focus", ["flowlist"], "Toggle mark on this flow")
- km.add("M", "view.marked.toggle", ["flowlist"], "Toggle viewing marked flows")
+ km.add("M", "view.properties.marked.toggle", ["flowlist"], "Toggle viewing marked flows")
km.add(
"n",
"console.command view.create get https://example.com/",
@@ -80,8 +80,8 @@ def map(km):
km.add("w", "console.command save.file @shown ", ["flowlist"], "Save listed flows to file")
km.add("V", "flow.revert @focus", ["flowlist", "flowview"], "Revert changes to this flow")
km.add("X", "flow.kill @focus", ["flowlist"], "Kill this flow")
- km.add("z", "view.remove @all", ["flowlist"], "Clear flow list")
- km.add("Z", "view.remove @hidden", ["flowlist"], "Purge all flows not showing")
+ km.add("z", "view.flows.remove @all", ["flowlist"], "Clear flow list")
+ km.add("Z", "view.flows.remove @hidden", ["flowlist"], "Purge all flows not showing")
km.add(
"|",
"console.command script.run @focus ",
@@ -100,7 +100,7 @@ def map(km):
)
km.add(
"f",
- "view.setval.toggle @focus fullcontents",
+ "view.settings.setval.toggle @focus fullcontents",
["flowview"],
"Toggle viewing full contents on this flow",
)
diff --git a/mitmproxy/tools/console/flowlist.py b/mitmproxy/tools/console/flowlist.py
index a9e48af4..e947a582 100644
--- a/mitmproxy/tools/console/flowlist.py
+++ b/mitmproxy/tools/console/flowlist.py
@@ -42,7 +42,7 @@ class FlowListWalker(urwid.ListWalker):
def positions(self, reverse=False):
# The stub implementation of positions can go once this issue is resolved:
# https://github.com/urwid/urwid/issues/294
- ret = range(len(self.master.view))
+ ret = range(self.master.commands.execute("view.properties.length"))
if reverse:
return reversed(ret)
return ret
@@ -57,19 +57,19 @@ class FlowListWalker(urwid.ListWalker):
return f, self.master.view.focus.index
def set_focus(self, index):
- if self.master.view.inbounds(index):
+ if self.master.commands.execute("view.properties.inbounds %d" % index):
self.master.view.focus.index = index
def get_next(self, pos):
pos = pos + 1
- if not self.master.view.inbounds(pos):
+ if not self.master.commands.execute("view.properties.inbounds %d" % pos):
return None, None
f = FlowItem(self.master, self.master.view[pos])
return f, pos
def get_prev(self, pos):
pos = pos - 1
- if not self.master.view.inbounds(pos):
+ if not self.master.commands.execute("view.properties.inbounds %d" % pos):
return None, None
f = FlowItem(self.master, self.master.view[pos])
return f, pos
@@ -87,9 +87,9 @@ class FlowListBox(urwid.ListBox, layoutwidget.LayoutWidget):
def keypress(self, size, key):
if key == "m_start":
- self.master.commands.execute("view.go 0")
+ self.master.commands.execute("view.focus.go 0")
elif key == "m_end":
- self.master.commands.execute("view.go -1")
+ self.master.commands.execute("view.focus.go -1")
elif key == "m_select":
self.master.commands.execute("console.view.flow @focus")
return urwid.ListBox.keypress(self, size, key)
diff --git a/mitmproxy/tools/console/flowview.py b/mitmproxy/tools/console/flowview.py
index b8ba7f12..87671c3b 100644
--- a/mitmproxy/tools/console/flowview.py
+++ b/mitmproxy/tools/console/flowview.py
@@ -98,7 +98,7 @@ class FlowDetails(tabs.Tabs):
msg, body = "", [urwid.Text([("error", "[content missing]")])]
return msg, body
else:
- full = self.master.commands.execute("view.getval @focus fullcontents false")
+ full = self.master.commands.execute("view.settings.getval @focus fullcontents false")
if full == "true":
limit = sys.maxsize
else:
diff --git a/mitmproxy/tools/console/statusbar.py b/mitmproxy/tools/console/statusbar.py
index ccf5e2e0..215cf500 100644
--- a/mitmproxy/tools/console/statusbar.py
+++ b/mitmproxy/tools/console/statusbar.py
@@ -271,7 +271,7 @@ class StatusBar(urwid.WidgetWrap):
return r
def redraw(self):
- fc = len(self.master.view)
+ fc = self.master.commands.execute("view.properties.length")
if self.master.view.focus.flow is None:
offset = 0
else:
@@ -283,7 +283,7 @@ class StatusBar(urwid.WidgetWrap):
arrow = common.SYMBOL_DOWN
marked = ""
- if self.master.view.show_marked:
+ if self.master.commands.execute("view.properties.marked"):
marked = "M"
t = [
diff --git a/mitmproxy/types.py b/mitmproxy/types.py
index 283e7e2e..f2a26b40 100644
--- a/mitmproxy/types.py
+++ b/mitmproxy/types.py
@@ -337,7 +337,7 @@ class _FlowType(_BaseFlowType):
def parse(self, manager: _CommandBase, t: type, s: str) -> flow.Flow:
try:
- flows = manager.call_strings("view.resolve", [s])
+ flows = manager.call_strings("view.flows.resolve", [s])
except exceptions.CommandError as e:
raise exceptions.TypeError from e
if len(flows) != 1:
@@ -356,7 +356,7 @@ class _FlowsType(_BaseFlowType):
def parse(self, manager: _CommandBase, t: type, s: str) -> typing.Sequence[flow.Flow]:
try:
- return manager.call_strings("view.resolve", [s])
+ return manager.call_strings("view.flows.resolve", [s])
except exceptions.CommandError as e:
raise exceptions.TypeError from e
diff --git a/release/README.md b/release/README.md
index 01a8d7dd..6f4e8f1a 100644
--- a/release/README.md
+++ b/release/README.md
@@ -7,14 +7,11 @@ release for! The command examples assume that you have a git remote called
- Verify that `mitmproxy/version.py` is correct
- Update CHANGELOG
- Verify that all CI tests pass
-- Create a major version branch - e.g. `v4.x`. Assuming you have a remote repo called `upstream` that points to the mitmproxy/mitmproxy repo::
+- If needed, create a major version branch - e.g. `v4.x`. Assuming you have a remote repo called `upstream` that points to the mitmproxy/mitmproxy repo::
- `git checkout -b v4.x upstream/master`
- `git push -u upstream v4.x`
- Tag the release and push to Github
- - For alphas, betas, and release candidates, use lightweight tags. This is
- necessary so that the .devXXXX counter does not reset.
- - For final releases, use annotated tags. This makes the .devXXXX counter reset.
- - `git tag -a v4.0.0 -m v4.0.0`
+ - `git tag v4.0.0`
- `git push upstream v4.0.0`
- Wait for tag CI to complete
@@ -39,7 +36,7 @@ release for! The command examples assume that you have a git remote called
## Docker
- The docker image is built on Travis and pushed to Docker Hub automatically.
- Please check https://hub.docker.com/r/mitmproxy/mitmproxy/tags/ about the latest version
-- Update `latest` tag: `docker tag mitmproxy/mitmproxy:<version number here> mitmproxy/mitmproxy:latest && docker push mitmproxy/mitmproxy:latest`
+- Update `latest` tag: `export VERSION=4.0.3 && docker pull mitmproxy/mitmproxy:$VERSION && docker tag mitmproxy/mitmproxy:$VERSION mitmproxy/mitmproxy:latest && docker push mitmproxy/mitmproxy:latest`
## Website
- Update version here:
diff --git a/release/cibuild.py b/release/cibuild.py
index 2d11d69a..37511086 100755
--- a/release/cibuild.py
+++ b/release/cibuild.py
@@ -63,6 +63,12 @@ class BuildEnviron:
self.travis_tag = travis_tag
self.travis_branch = travis_branch
+
+ if travis_tag and travis_tag != travis_branch:
+ raise ValueError(
+ f"Something is wrong - TRAVIS_TAG={travis_tag}, but TRAVIS_BRANCH={travis_branch}"
+ )
+
self.travis_pull_request = travis_pull_request
self.should_build_wheel = should_build_wheel
@@ -184,25 +190,29 @@ class BuildEnviron:
"""
with open(pathlib.Path(self.root_dir) / "mitmproxy" / "version.py") as f:
contents = f.read()
-
version = re.search(r'^VERSION = "(.+?)"', contents, re.M).group(1)
- if self.tag:
- # For (tagged) releases, we are strict:
- # 1. The tagname must match the version in mitmproxy/version.py
- # 2. The version info must be in canonical form (as recommended in PEP 440).
-
- if version != self.tag:
+ if self.is_prod_release:
+ # For production releases, we require strict version equality
+ if self.version != version:
raise ValueError(f"Tag is {self.tag}, but mitmproxy/version.py is {version}.")
- try:
- parver.Version.parse(version, strict=True)
- except parver.ParseError as e:
- raise ValueError(str(e)) from e
- else:
- # For snapshots, we only ensure that mitmproxy/version.py contains a dev release.
+ elif not self.is_maintenance_branch:
+ # Commits on maintenance branches don't need the dev suffix. This
+ # allows us to incorporate and test commits between tagged releases.
+ # For snapshots, we only ensure that mitmproxy/version.py contains a
+ # dev release.
version_info = parver.Version.parse(version)
if not version_info.is_devrelease:
- raise ValueError("Releases must be tagged.")
+ raise ValueError(f"Non-production releases must have dev suffix: {version}")
+
+ @property
+ def is_maintenance_branch(self) -> bool:
+ """
+ Is this an untagged commit on a maintenance branch?
+ """
+ if not self.tag and self.branch and re.match(r"v\d+\.x", self.branch):
+ return True
+ return False
@property
def has_docker_creds(self) -> bool:
@@ -210,7 +220,7 @@ class BuildEnviron:
@property
def is_prod_release(self) -> bool:
- if not self.tag:
+ if not (self.tag and self.tag.startswith("v")):
return False
try:
v = parver.Version.parse(self.version, strict=True)
@@ -265,10 +275,18 @@ class BuildEnviron:
@property
def version(self):
- name = self.tag or self.branch
- if not name:
+ if self.tag:
+ if self.tag.startswith("v"):
+ try:
+ parver.Version.parse(self.tag[1:], strict=True)
+ except parver.ParseError as e:
+ return self.tag
+ return self.tag[1:]
+ return self.tag
+ elif self.branch:
+ return self.branch
+ else:
raise BuildError("We're on neither a tag nor a branch - could not establish version")
- return name
def build_wheel(be: BuildEnviron): # pragma: no cover
diff --git a/test/mitmproxy/addons/test_view.py b/test/mitmproxy/addons/test_view.py
index bd724950..976c14b7 100644
--- a/test/mitmproxy/addons/test_view.py
+++ b/test/mitmproxy/addons/test_view.py
@@ -107,13 +107,12 @@ def test_simple():
def test_filter():
v = view.View()
- f = flowfilter.parse("~m get")
v.request(tft(method="get"))
v.request(tft(method="put"))
v.request(tft(method="get"))
v.request(tft(method="put"))
assert(len(v)) == 4
- v.set_filter(f)
+ v.set_filter_cmd("~m get")
assert [i.request.method for i in v] == ["GET", "GET"]
assert len(v._store) == 4
v.set_filter(None)
@@ -124,6 +123,9 @@ def test_filter():
v.toggle_marked()
assert len(v) == 4
+ with pytest.raises(exceptions.CommandError):
+ v.set_filter_cmd("~notafilter regex")
+
v[1].marked = True
v.toggle_marked()
assert len(v) == 1
@@ -303,23 +305,26 @@ def test_setgetval():
def test_order():
v = view.View()
- with taddons.context(v) as tctx:
- v.request(tft(method="get", start=1))
- v.request(tft(method="put", start=2))
- v.request(tft(method="get", start=3))
- v.request(tft(method="put", start=4))
- assert [i.request.timestamp_start for i in v] == [1, 2, 3, 4]
-
- tctx.configure(v, view_order="method")
- assert [i.request.method for i in v] == ["GET", "GET", "PUT", "PUT"]
- v.set_reversed(True)
- assert [i.request.method for i in v] == ["PUT", "PUT", "GET", "GET"]
+ v.request(tft(method="get", start=1))
+ v.request(tft(method="put", start=2))
+ v.request(tft(method="get", start=3))
+ v.request(tft(method="put", start=4))
+ assert [i.request.timestamp_start for i in v] == [1, 2, 3, 4]
+
+ v.set_order("method")
+ assert v.get_order() == "method"
+ assert [i.request.method for i in v] == ["GET", "GET", "PUT", "PUT"]
+ v.set_reversed(True)
+ assert [i.request.method for i in v] == ["PUT", "PUT", "GET", "GET"]
- tctx.configure(v, view_order="time")
- assert [i.request.timestamp_start for i in v] == [4, 3, 2, 1]
+ v.set_order("time")
+ assert v.get_order() == "time"
+ assert [i.request.timestamp_start for i in v] == [4, 3, 2, 1]
- v.set_reversed(False)
- assert [i.request.timestamp_start for i in v] == [1, 2, 3, 4]
+ v.set_reversed(False)
+ assert [i.request.timestamp_start for i in v] == [1, 2, 3, 4]
+ with pytest.raises(exceptions.CommandError):
+ v.set_order("not_an_order")
def test_reversed():
@@ -551,6 +556,17 @@ def test_settings():
assert not v.settings.keys()
+def test_properties():
+ v = view.View()
+ f = tft()
+ v.request(f)
+ assert v.get_length() == 1
+ assert not v.get_marked()
+ v.toggle_marked()
+ assert v.get_length() == 0
+ assert v.get_marked()
+
+
def test_configure():
v = view.View()
with taddons.context(v) as tctx:
diff --git a/test/mitmproxy/test_command.py b/test/mitmproxy/test_command.py
index 029dbafd..d9dcf5f9 100644
--- a/test/mitmproxy/test_command.py
+++ b/test/mitmproxy/test_command.py
@@ -313,7 +313,7 @@ def test_typename():
class DummyConsole:
- @command.command("view.resolve")
+ @command.command("view.flows.resolve")
def resolve(self, spec: str) -> typing.Sequence[flow.Flow]:
n = int(spec)
return [tflow.tflow(resp=True)] * n
diff --git a/test/mitmproxy/test_types.py b/test/mitmproxy/test_types.py
index 35ff3241..571985fb 100644
--- a/test/mitmproxy/test_types.py
+++ b/test/mitmproxy/test_types.py
@@ -146,7 +146,7 @@ def test_strseq():
class DummyConsole:
- @command.command("view.resolve")
+ @command.command("view.flows.resolve")
def resolve(self, spec: str) -> typing.Sequence[flow.Flow]:
if spec == "err":
raise mitmproxy.exceptions.CommandError()
diff --git a/test/release/test_cibuild.py b/test/release/test_cibuild.py
index efa2f072..cfa24e63 100644
--- a/test/release/test_cibuild.py
+++ b/test/release/test_cibuild.py
@@ -36,6 +36,14 @@ def test_buildenviron_common():
with pytest.raises(cibuild.BuildError):
be.platform_tag
+ with pytest.raises(ValueError, match="TRAVIS_TAG"):
+ be = cibuild.BuildEnviron(
+ system="Linux",
+ root_dir="/foo",
+ travis_tag="one",
+ travis_branch="two",
+ )
+
def test_buildenviron_pr():
# Simulates a PR. We build everything, but don't have access to secret
@@ -56,6 +64,7 @@ def test_buildenviron_pr():
)
assert be.is_pull_request
assert not be.is_prod_release
+ assert not be.is_maintenance_branch
def test_buildenviron_commit():
@@ -75,6 +84,7 @@ def test_buildenviron_commit():
assert not be.should_upload_pypi
assert be.should_upload_docker
assert not be.is_prod_release
+ assert not be.is_maintenance_branch
def test_buildenviron_releasetag():
@@ -82,8 +92,8 @@ def test_buildenviron_releasetag():
be = cibuild.BuildEnviron(
system="Linux",
root_dir="/foo",
- travis_tag="0.0.1",
- travis_branch="v0.x",
+ travis_tag="v0.0.1",
+ travis_branch="v0.0.1",
should_build_wheel=True,
should_build_docker=True,
should_build_pyinstaller=True,
@@ -91,18 +101,44 @@ def test_buildenviron_releasetag():
docker_username="foo",
docker_password="bar",
)
- assert be.tag == "0.0.1"
- assert be.branch == "v0.x"
+ assert be.tag == "v0.0.1"
+ assert be.branch == "v0.0.1"
assert be.version == "0.0.1"
assert be.upload_dir == "0.0.1"
assert be.docker_tag == "mitmproxy/mitmproxy:0.0.1"
assert be.should_upload_pypi
assert be.should_upload_docker
assert be.is_prod_release
+ assert not be.is_maintenance_branch
+
+
+def test_buildenviron_namedtag():
+ # Simulates a non-release tag on a branch.
+ be = cibuild.BuildEnviron(
+ system="Linux",
+ root_dir="/foo",
+ travis_tag="anyname",
+ travis_branch="anyname",
+ should_build_wheel=True,
+ should_build_docker=True,
+ should_build_pyinstaller=True,
+ has_twine_creds=True,
+ docker_username="foo",
+ docker_password="bar",
+ )
+ assert be.tag == "anyname"
+ assert be.branch == "anyname"
+ assert be.version == "anyname"
+ assert be.upload_dir == "anyname"
+ assert be.docker_tag == "mitmproxy/mitmproxy:anyname"
+ assert not be.should_upload_pypi
+ assert not be.should_upload_docker
+ assert not be.is_prod_release
+ assert not be.is_maintenance_branch
-def test_buildenviron_branch():
- # Simulates a development branch on the main repo
+def test_buildenviron_dev_branch():
+ # Simulates a commit on a development branch on the main repo
be = cibuild.BuildEnviron(
system="Linux",
root_dir="/foo",
@@ -121,6 +157,30 @@ def test_buildenviron_branch():
assert be.upload_dir == "branches/mybranch"
assert not be.should_upload_pypi
assert not be.should_upload_docker
+ assert not be.is_maintenance_branch
+
+
+def test_buildenviron_maintenance_branch():
+ # Simulates a commit on a release maintenance branch on the main repo
+ be = cibuild.BuildEnviron(
+ system="Linux",
+ root_dir="/foo",
+ travis_tag="",
+ travis_branch="v0.x",
+ should_build_wheel=True,
+ should_build_docker=True,
+ should_build_pyinstaller=True,
+ has_twine_creds=True,
+ docker_username="foo",
+ docker_password="bar",
+ )
+ assert be.tag == ""
+ assert be.branch == "v0.x"
+ assert be.version == "v0.x"
+ assert be.upload_dir == "branches/v0.x"
+ assert not be.should_upload_pypi
+ assert not be.should_upload_docker
+ assert be.is_maintenance_branch
def test_buildenviron_osx(tmpdir):
@@ -128,7 +188,7 @@ def test_buildenviron_osx(tmpdir):
system="Darwin",
root_dir="/foo",
travis_tag="0.0.1",
- travis_branch="v0.x",
+ travis_branch="0.0.1",
)
assert be.platform_tag == "osx"
assert be.bdists == {
@@ -146,8 +206,8 @@ def test_buildenviron_windows(tmpdir):
be = cibuild.BuildEnviron(
system="Windows",
root_dir="/foo",
- travis_tag="0.0.1",
- travis_branch="v0.x",
+ travis_tag="v0.0.1",
+ travis_branch="v0.0.1",
)
assert be.platform_tag == "windows"
assert be.bdists == {
@@ -163,18 +223,21 @@ def test_buildenviron_windows(tmpdir):
@pytest.mark.parametrize("version, tag, ok", [
("3.0.0.dev", "", True), # regular snapshot
- ("3.0.0.dev", "3.0.0", False), # forgot to remove ".dev" on bump
+ ("3.0.0.dev", "v3.0.0", False), # forgot to remove ".dev" on bump
("3.0.0", "", False), # forgot to re-add ".dev"
- ("3.0.0", "4.0.0", False), # version mismatch
- ("3.0.0", "3.0.0", True), # regular release
- ("3.0.0.rc1", "3.0.0.rc1", False), # non-canonical.
+ ("3.0.0", "v4.0.0", False), # version mismatch
+ ("3.0.0", "v3.0.0", True), # regular release
+ ("3.0.0.rc1", "v3.0.0.rc1", False), # non-canonical.
+ ("3.0.0.dev", "anyname", True), # tagged test/dev release
+ ("3.0.0", "3.0.0", False), # tagged, but without v prefix
])
def test_buildenviron_check_version(version, tag, ok, tmpdir):
tmpdir.mkdir("mitmproxy").join("version.py").write(f'VERSION = "{version}"')
be = cibuild.BuildEnviron(
root_dir=tmpdir,
- travis_tag=tag
+ travis_tag=tag,
+ travis_branch=tag or "branch",
)
if ok:
be.check_version()