Skip to content

Commit eb836c0

Browse files
authored
Flatten views structure: reduce space complexity by 2 dimensions (#8258)
Part of #7585 Closes #5903 Must be merged in coordination with Switch views repo docker-addonstore#36 Structure change The current add-on views structure is too wide. It has storage complexity of O(numAddonFiles * numLanguages * numNVDAAPIVersions * numChannels). This should just be O(numAddonFiles). This PR reduces the structure to O(numAddonFiles * numLanguages). The current file structure for add-on views is: views/en/2019.1.0/UIANotificationSwitch/stable.json Generated from files committed to this branch as: addons/AbsoluteYoutube/2026.2.0.json File paths in the current views structure now serve as symlinks which point to files like addons/UIANotificationSwitch/2026.1/en.json which is generated from the translations in addons/AbsoluteYoutube/2026.2.0.json Improvements Size The new views size is 264 MB, the old views size was 13GB. Checked with: (Get-ChildItem . -Recurse -Force -File | Measure-Object Length -Sum).Sum). Transform step speed The old transform step took about 1.5-2h. The new transform step takes about 10-15min.
1 parent 3a13a78 commit eb836c0

12 files changed

Lines changed: 389 additions & 136 deletions

File tree

.github/workflows/transformDataToViews.yml

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ concurrency:
1010

1111
jobs:
1212
transformAndPush:
13-
runs-on: windows-latest
13+
runs-on: ubuntu-latest
14+
defaults:
15+
run:
16+
shell: bash
17+
permissions:
18+
contents: write
19+
pull-requests: write
1420
env:
1521
# this is a git '--pretty=format' string
1622
# %h is SHA, %n is newline,
@@ -24,38 +30,72 @@ jobs:
2430
uses: actions/checkout@v5
2531
with:
2632
submodules: true
27-
- name: Checkout views branch into separate folder
33+
- name: Checkout addonstore-views repository
2834
uses: actions/checkout@v5
2935
with:
36+
repository: nvaccess/addonstore-views
3037
path: views
31-
ref: views
38+
ref: main
39+
token: ${{ secrets.VIEWS_PUSH_TOKEN }}
40+
- name: Install system deps for lxml
41+
run: |
42+
sudo apt-get update
43+
sudo apt-get install -y --no-install-recommends \
44+
libxml2-dev libxslt1-dev zlib1g-dev
3245
- name: Install the latest version of uv
3346
uses: astral-sh/setup-uv@v6
3447
- name: Set up Python ${{ matrix.python-version }}
3548
uses: actions/setup-python@v5
3649
with:
3750
python-version: ${{ matrix.python-version }}
38-
architecture: x86
3951
- name: Install requirements and run transformation
4052
run: |
53+
set -euo pipefail
4154
uv sync
42-
# empty the views git folder directory
43-
Try { Remove-Item views/views/ -Recurse -ErrorAction stop }
44-
Catch [System.Management.Automation.ItemNotFoundException] { $null }
45-
uv run --directory transform python -m src.transform --loglevel ${{ env.logLevel }} nvdaAPIVersions.json ../addons ../views/views
55+
56+
# generate transformed data from this repository's addons metadata
57+
rm -rf generated/
58+
uv run --directory transform python -m src.transform --loglevel "${{ env.logLevel }}" nvdaAPIVersions.json ../addons ../generated
59+
60+
# replace generated data directories in addonstore-views
61+
rm -rf views/addons/ views/views/
62+
cp -R generated/addons views/addons
63+
cp -R generated/views views/views
4664
env:
4765
logLevel: ${{ runner.debug && 'DEBUG' || 'INFO' }}
4866
- name: Copy files
4967
run: |
50-
copy ./transform/nvdaAPIVersions.json ./views/nvdaAPIVersions.json
51-
copy ./transform/docs/output.md ./views/output.md
52-
copy ./readme.md ./views/readme.md
53-
- name: Commit and push
68+
set -euo pipefail
69+
cp ./transform/nvdaAPIVersions.json ./views/nvdaAPIVersions.json
70+
cp ./transform/docs/output.md ./views/output.md
71+
cp ./README.md ./views/README.md
72+
- name: Create PR and enable auto-merge
73+
env:
74+
GH_TOKEN: ${{ secrets.VIEWS_PUSH_TOKEN }}
5475
run: |
76+
set -euo pipefail
5577
git log HEAD --pretty=format:"${{ env.COMMIT_FORMAT }}" -1 > commitMsg.txt
5678
cd views
5779
git config user.name github-actions
5880
git config user.email github-actions@github.com
5981
git add .
60-
git commit -F ../commitMsg.txt
61-
git push
82+
if git diff --staged --quiet; then
83+
echo "No generated changes; skipping PR creation."
84+
exit 0
85+
fi
86+
87+
branchName="transformViews${{ github.run_id }}-${{ github.run_attempt }}"
88+
git checkout -b "$branchName"
89+
# TODO: Temporarily suppress output during first commit as testing logs are too big.
90+
# Remove quiet and gpg sign flags later.
91+
git commit --quiet --no-gpg-sign -F ../commitMsg.txt
92+
git push --set-upstream origin "$branchName"
93+
94+
prUrl=$(gh pr create \
95+
--repo nvaccess/addonstore-views \
96+
--base main \
97+
--head "$branchName" \
98+
--title "[Automated] Transform add-on data to views" \
99+
--body-file ../commitMsg.txt)
100+
101+
gh pr merge --auto --squash --delete-branch "$prUrl"

.python-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
cpython-3.13-windows-x86_64-none
1+
cpython-3.13

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Add-on Store
22

3-
The addon-datastore repository is a data pipeline of submitting, validating and transforming add-on data to views.
4-
These views are hosted on the NV Access server and are available in the NVDA Add-on Store.
3+
The addon-datastore repository is a data pipeline for submitting and validating add-on data.
4+
Transformed add-on store data is produced with add-on files and symlinked views for hosting on the NV Access server.
55

66
Please note: the NVDA project including the Add-on Store has a [Citizen and Contributor Code of Conduct](https://github.com/nvaccess/nvda/blob/master/CODE_OF_CONDUCT.md).
77
NV Access expects that all contributors and other community members will read and abide by the rules set out in this document while participating in the project or contributing add-ons.

docs/design/designOverview.md

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,27 +63,34 @@ Aims:
6363
- While this is technically not necessary, it provides a good separation from implementation.
6464
If we wished to change our storage mechanism, we would not be breaking old versions of NVDA.
6565

66-
6766
## API data generation details
6867

69-
Triggered by a new commit to the `master` branch, [a GitHub workflow](../../.github/workflows/transformDataToViews.yml), [addon-datastore-transform](https://github.com/nvaccess/addon-datastore-transform), transforms the data into the required views.
68+
Triggered by a new commit to the `master` branch, [a GitHub workflow](../../.github/workflows/transformDataToViews.yml), and the [transform](../../transform/) module, transforms the data into add-on files and projected views.
7069

7170
For each NVDA API version and channel, the add-on metadata with the highest version number is written.
7271

73-
These views are then committed by the GitHub Action to the [views branch](https://github.com/nvaccess/addon-datastore/tree/views).
72+
This transformed data is then committed by the GitHub Action to the [addonstore-views](https://github.com/nvaccess/addonstore-views) main branch.
7473

7574
### Data views
76-
The following views will only be available on the [views branch](https://github.com/nvaccess/addon-datastore/tree/views) and located in a `views` folder.
77-
Required transformations of the data:
78-
- `/NVDA API Version/addon-1-ID/stable.json`
79-
- `/NVDA API Version/addon-1-ID/beta.json`
80-
- `/NVDA API Version/addon-2-ID/stable.json`
75+
76+
The generated data is stored in two top-level folders:
77+
78+
- `addons`: add-on data files by add-on version and language.
79+
- `views`: compatibility and latest projections as relative symlinks into `addons`.
80+
81+
The following projected views are available in the `views` folder.
82+
83+
The views folder is expected to have the following structure:
84+
85+
`/views/<lang>/<NvdaAPIVersion>/<addonId>/<channel>.json`
8186

8287
Notes:
88+
8389
- 'NVDA API Version' will be something like '2019.3', there will be one folder for each NVDA API Version.
8490
- The `beta.json` and `stable.json` contain the information necessary for a store entry.
8591
- The contents for each add-on will include all the technical details required for NVDA to download, verify file integrity, and install.
86-
- The file will include translations (if available) for the displayable metadata.
92+
- View files are relative symlinks to files in `addons`.
93+
- Files use translations (if available) for displayable metadata.
8794

8895
This simplifies the processing on the hosting (E.G NV Access) server.
8996

@@ -116,16 +123,14 @@ Channel can be: all, dev, stable or beta.
116123
- Example: <https://addonStore.nvaccess.org/en/all/latest.json>
117124

118125
### `GET` cacheHash
126+
119127
Returns a hash used for cache breaking.
120128
This hash will change whenever new add-on data is available.
121-
The hash should match the latest commit hash of the [views branch](https://github.com/nvaccess/addon-datastore/commits/views).
129+
For testing purposes, the hash should match the latest commit hash of the transformed [addonstore-views](https://github.com/nvaccess/addonstore-views) branch.
122130

123131
- <https://addonStore.nvaccess.org/cacheHash.json>
124132
- Example return value: `"5fcf12f"`
125133

126-
### Legacy
127-
This endpoint mirrors the legacy [get.php, from the addonFiles repository](https://github.com/nvaccess/addonFiles/blob/master/get.php).
128-
129134
#### `addonslist` end-point
130135

131136
The `addonslist` parameter generates a list of list of add-ons using the same logic as the [latest end-point](#get-latest) for all channels.

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ reportMissingTypeStubs = false
6868

6969
[tool.uv]
7070
default-groups = "all"
71-
environments = ["sys_platform == 'win32'"]
7271
required-version = ">=0.8"
7372

7473
[tool.setuptools]

transform/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
# Transforming data to views
22

3-
This repository primarily exists to transform data from [nvaccess/addon-datastore:master](https://github.com/nvaccess/addon-datastore) to views located at [nvaccess/addon-datastore:views](https://github.com/nvaccess/addon-datastore/tree/views).
3+
This module transforms add-on metadata into an output data layout with add-on files and views.
4+
The output is designed to be published from a single branch.
45

56
For each NVDA version that needs to be supported by the add-on store, an entry must be added to [`nvdaAPIVersions.json`](./nvdaAPIVersions.json).
67
This includes patch versions.
78

9+
This module should be run from linux, as symlinks are created for the server component.
10+
811
## Overview
912

1013
For each version of NVDA, the meta-data of the most recent (the highest version number) of each add-on is automatically

transform/docs/output.md

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,55 @@
1+
# Output
2+
13
The output for running the transformation is described as follows.
2-
This is written to a given directory, and removes existing json data from the directory in the process.
4+
This is written to a given directory that must be new/empty; the transformation creates this directory and fails if it already exists.
5+
Callers are responsible for deleting any previous output directory before running the transformation.
36

47
## Output file structure
8+
59
The following subdirectories and files are created:
6-
- `/NVDA API Version/addon-1-ID/stable.json`
7-
- `/NVDA API Version/addon-1-ID/beta.json`
8-
- `/NVDA API Version/addon-2-ID/stable.json`
9-
eg: `/2020.3.0/nvdaOCR/stable.json`
10+
11+
Translated add-on files:
12+
13+
- `/addons/addon-1-ID/<addonVersion>/en.json`
14+
- `/addons/addon-1-ID/<addonVersion>/ar.json` (when a translation exists)
15+
- `/addons/addon-2-ID/<addonVersion>/en.json`
16+
17+
Symlink end views which point to translated files:
18+
19+
- `/views/en/<NvdaAPIVersion>/addon-1-ID/stable.json`
20+
- `/views/en/<NvdaAPIVersion>/addon-1-ID/beta.json`
21+
- `/views/en/<NvdaAPIVersion>/addon-2-ID/stable.json`
22+
- `/views/ar/<NvdaAPIVersion>addon-1-ID/stable.json`
23+
24+
Examples:
25+
26+
- `/addons/nvdaOCR/1.3.5/en.json`
27+
- `/views/en/2020.3.0/nvdaOCR/stable.json`
1028

1129
Where `NVDA API Version` may be:
30+
1231
- `2022.1.0`: A major release.
1332
- `2022.1.3`: A patch release.
1433

15-
The system differentiates patch releases from major releases to cater to the (very unlikely) event of requireing a breaking change or introduction to the NVDA add-on API.
34+
The system differentiates patch releases from major releases to cater to the (very unlikely) event of requiring a breaking change or introduction to the NVDA add-on API.
1635

1736
## Output file data
18-
Each addon file is the addon data taken from input that is the latest compatible version, with the given requirements `(NVDA API Version, addon-ID, stable|beta|dev)`.
19-
The transformed data file content will be the same as the input.
20-
The contents for each addon file includes all the technical details required for NVDA to download, verify file integrity, and install.
37+
38+
Add-on files in `/addons` contain transformed add-on metadata by add-on version and language.
39+
Views in `/views` are relative symlinks to files in `/addons`.
40+
41+
For each required view `(language, NVDA API Version, addon-ID, channel)`, the view symlink points at a single file:
42+
43+
- Prefer exact language translation
44+
- Otherwise prefer language without locale (`pt_BR` -> `pt`)
45+
- Otherwise fallback to `en`
46+
47+
Each transformed add-on file includes all technical details required for NVDA to download, verify file integrity, and install.
2148
It also contains the information necessary for a store entry.
22-
Later, translated versions will become available.
2349

2450
## Output notes
51+
2552
This structure simplifies the processing on the hosting (e.g. NV Access) server.
26-
To fetch the latest add-ons for `<NVDA API Version X>`, the server can concatenate the appropriate JSON files that match a glob: `/<NVDA API Version X>/*/stable.json`.
27-
Similarly, to fetch the latest version of an add-on with `<Addon-ID>` for `<NVDA API Version X>`. The server can return the data at `/<NVDA API Version X>/<addon-ID>/stable.json`.
53+
To fetch the latest add-ons for `<NVDA API Version X>`, the server can concatenate the appropriate JSON files that match a glob: `/views/<lang>/<NVDA API Version X>/*/stable.json`.
54+
Similarly, to fetch the latest version of an add-on with `<Addon-ID>` for `<NVDA API Version X>`, the server can return the data at `/views/<lang>/<NVDA API Version X>/<addon-ID>/stable.json`.
2855
Using the NV Access server as the endpoint for this is important in case the implementation has to change or be migrated away from GitHub for some reason.

transform/src/tests/test_datastructures.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def test_toStr_patch_optional(self):
2525
"""Confirm that versions as string always include the patch number, 0 by default.
2626
2727
Even if the patch isn't specified, it should be included
28-
so that the output is consistent - e.g. /views/2021.1.3/stable.json"""
28+
so that the output is consistent - e.g. /views/en/2021.1.3/addonId/stable.json"""
2929
self.assertEqual(str(MajorMinorPatch(13, 2)), "13.2.0")
3030

3131
def test_fromDict(self):

0 commit comments

Comments
 (0)