OpenCyber Academy
Bachelor's · Chapter 6 · English
Course code: OCA-106Chapter 06: OCA-106-C006

Chapter 6 — Discrete structures

Open, free learning for anyone who wants to protect people and technology. No account is required to read this text or its worked answers.

Lesson map

Lesson 06 published. Map, example, EX016.

flowchart TD
  N0["Learning outcomes"]
  N1["6.1 Versions as tuples"]
  N2["6.2 Ranges as sets"]
  N3["Lab — Lab — version ranges"]
  N4["6.3 Lab result"]
  N5["6.4 Why it matters"]
  N6["6.5 Lockfiles"]
  N7["Pre-releases"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
  N7 --> EX["Worked example"]
  EX --> Q["Exercise EX016"]

Logical drawing

flowchart LR
  N0["Learning outcomes"]
  N1["6.1 Versions as tuples"]
  N2["6.2 Ranges as sets"]
  N3["Lab — Lab — version ranges"]
  N4["6.3 Lab result"]
  N5["6.4 Why it matters"]
  N6["6.5 Lockfiles"]
  N7["Pre-releases"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX016"]
[Learning outcomes]
    |
    v
[6.1 Versions as tuples]
    |
    v
[6.2 Ranges as sets]
    |
    v
[Lab — Lab — version ranges]
    |
    v
[6.3 Lab result]
    |
    v
[6.4 Why it matters]
    |
    v
[6.5 Lockfiles]
    |
    v
[Pre-releases]
    |
    v
[Worked example] --> [Exercise EX016]

Learning outcomes

You will (1) represent version ranges as sets; (2) compare versions as ordered tuples; (3) reason about which version a range selects; (4) avoid text-ordering mistakes.

6.1 Versions as tuples

Semantic versions such as 1.10.2 have three numbers: major, minor and patch. They are ordered as tuples: compare major first, then minor, then patch. As text, "1.10.2" sorts before "1.4.7" because the character "1" comes before "4", which is wrong.

Lena writes a small comparison function that splits a version into its three numbers and compares them in order. Comparing tuples gives the right order in every case, including versions with numbers of different lengths. She tests it against the examples in the lab and adds the case of equal versions, which must compare as neither older nor newer. The function is later reused in the society's dependency report.

6.2 Ranges as sets

A caret range ^1.4.0 means the set of versions at least 1.4.0 and below 2.0.0. Package managers pick the highest version in the set that is available. Understanding the set tells you which fixes you receive automatically and which need a manual change.

For CampusEats, a caret range on a payment library means patch and minor releases arrive automatically at the next install, while a new major version needs a deliberate upgrade and testing. Some teams prefer narrower tilde ranges, which allow only patches, trading automatic fixes for fewer surprises.

Lab — Lab — version ranges

"""Which available versions satisfy a caret range ^1.4.0 (>=1.4.0, <2.0.0)? Synthetic versions."""
def v(s): return tuple(int(x) for x in s.split("."))
available = ["1.3.9", "1.4.0", "1.4.7", "1.10.2", "2.0.0", "2.1.0"]
lo, hi = v("1.4.0"), v("2.0.0")
ok = [a for a in available if lo <= v(a) < hi]
print("satisfying:", ok)
print("highest chosen:", max(ok, key=v))
print("text sort would pick:", max(ok))

Run with python3 lab_semver.py. Verified output:

satisfying: ['1.4.0', '1.4.7', '1.10.2']
highest chosen: 1.10.2
text sort would pick: 1.4.7

6.3 Lab result

Of six versions, three satisfy ^1.4.0: 1.4.0, 1.4.7 and 1.10.2. The highest, chosen correctly by tuple order, is 1.10.2. Text sorting would wrongly pick 1.4.7, which could leave a project on an older, possibly vulnerable version.

Lena adds the six versions as a test case so the comparison cannot regress unnoticed. She also adds a case with a pre-release version, since such versions usually sit outside a normal range and are a common source of confusion when a fix is published first as a test release.

6.4 Why it matters

If a fix is released in 1.10.2 and a tool sorts as text, it would think 1.4.7 is newest and report the project as up to date. Kula et al. (2018) showed that many projects already lag behind fixes; errors like this make it worse.

Lena checks the society's dependency report and finds it uses proper version comparison. It also adds a test that fails if version strings are ever sorted as text, so a future change to the tool cannot reintroduce the mistake. A report that wrongly says a project is current is worse than no report, because it discourages anyone from checking.

6.5 Lockfiles

A lockfile records the exact version chosen, so builds are repeatable. Ranges decide what may be chosen; lockfiles record what was chosen.

CampusEats commits its lockfile, and the build installs exactly the locked versions. Updating a dependency is then a visible change in review: the lockfile diff shows which versions moved. The integrity check in Lena's first week depended on this too, since the lockfile also records the expected hash of each package, and the build refused a download that did not match it.

Pre-releases

Versions like 2.0.0-beta are pre-releases and usually excluded from ranges unless asked for, so tests should include them as edge cases.

Updating safely

When a range allows a new minor version, CampusEats updates the lockfile weekly in a separate pull request, so tests can confirm nothing broke before the change is merged.

Worked example

Lena finds a small internal script that reported "latest version" for the society's dashboard by text sorting. She replaces it with tuple comparison and adds a test with 1.4.7 versus 1.10.2.

Synthetic case. Versions are synthetic.

Errors and limits

Some ecosystems use pre-release labels and other schemes; follow each ecosystem's rules.

Exercises

Synthesis

Treat versions as ordered tuples and ranges as sets, and record exact choices.

Continue studying — chapter 06 of 40

← Chapter 05 (draft) · Course chapter index · Chapter 07 (draft) →

Worked answers

EX016. 1.4.0, 1.4.7 and 1.10.2.

EX017. 1.4.7, because text compares characters, so "1.10" seems lower than "1.4".

EX018. Ranges decide which versions may be chosen; lockfiles record the exact version chosen.