├── .coveragerc
├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ ├── change-coverage.yml
│ ├── execute-tests.yml
│ ├── test-linux.yml
│ ├── test-osx.yml
│ └── test-windows.yml
├── .gitignore
├── .mypy.ini
├── .noserc
├── .pre-commit-hooks.yaml
├── .project
├── .pydevproject
├── .pydocstyle.ini
├── .pylint.ini
├── .readthedocs.yaml
├── .scrutinizer.yml
├── .tool-versions
├── .verchew.ini
├── .vscode
└── settings.json
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Doorstop.sublime-project
├── LICENSE.md
├── MANIFEST.in
├── Makefile
├── Procfile
├── README.md
├── bin
├── checksum
├── example-adapter.wsgi
├── open
├── post_compile
└── verchew
├── docs
├── about
│ ├── changelog.md
│ ├── contributing.md
│ └── license.md
├── api
│ └── scripting.md
├── assets
│ └── logo.xcf
├── cli
│ ├── creation.md
│ ├── interchange.md
│ ├── publishing.md
│ ├── reordering.md
│ └── validation.md
├── examples.md
├── generate.py
├── getting-started
│ ├── installation.md
│ ├── quickstart.md
│ └── setup.md
├── gui
│ ├── desktop-gui.png
│ └── overview.md
├── images
│ └── logo-black-white.png
├── index.md
├── reference
│ ├── document.md
│ ├── item.md
│ └── tree.md
├── requirements.txt
└── web.md
├── doorstop
├── __init__.py
├── cli
│ ├── __init__.py
│ ├── commands.py
│ ├── main.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── docs
│ │ │ ├── .doorstop.yml
│ │ │ ├── HLT001.yml
│ │ │ ├── HLT002.yml
│ │ │ ├── HLT003.yml
│ │ │ ├── HLT004.yml
│ │ │ └── HLT005.yml
│ │ ├── files
│ │ │ ├── A001.txt
│ │ │ ├── A002.txt
│ │ │ ├── B001.txt
│ │ │ ├── B002.txt
│ │ │ ├── C001.txt
│ │ │ ├── C002.txt
│ │ │ ├── C003.txt
│ │ │ ├── exported-map.csv
│ │ │ ├── exported.csv
│ │ │ ├── exported.tsv
│ │ │ ├── exported.xlsx
│ │ │ └── template.yml
│ │ ├── test_all.py
│ │ ├── test_main.py
│ │ ├── test_utilities.py
│ │ └── tutorial.py
│ └── utilities.py
├── common.py
├── core
│ ├── __init__.py
│ ├── base.py
│ ├── builder.py
│ ├── document.py
│ ├── editor.py
│ ├── exporter.py
│ ├── files
│ │ └── templates
│ │ │ ├── html
│ │ │ ├── bootstrap.bundle.min.js
│ │ │ ├── bootstrap.min.css
│ │ │ ├── doorstop.css
│ │ │ ├── general.css
│ │ │ ├── jquery.min.js
│ │ │ ├── logo-black-white.png
│ │ │ ├── output
│ │ │ │ ├── chtml.js
│ │ │ │ ├── chtml
│ │ │ │ │ └── fonts
│ │ │ │ │ │ ├── tex.js
│ │ │ │ │ │ └── woff-v2
│ │ │ │ │ │ ├── MathJax_AMS-Regular.woff
│ │ │ │ │ │ ├── MathJax_Calligraphic-Bold.woff
│ │ │ │ │ │ ├── MathJax_Calligraphic-Regular.woff
│ │ │ │ │ │ ├── MathJax_Fraktur-Bold.woff
│ │ │ │ │ │ ├── MathJax_Fraktur-Regular.woff
│ │ │ │ │ │ ├── MathJax_Main-Bold.woff
│ │ │ │ │ │ ├── MathJax_Main-Italic.woff
│ │ │ │ │ │ ├── MathJax_Main-Regular.woff
│ │ │ │ │ │ ├── MathJax_Math-BoldItalic.woff
│ │ │ │ │ │ ├── MathJax_Math-Italic.woff
│ │ │ │ │ │ ├── MathJax_Math-Regular.woff
│ │ │ │ │ │ ├── MathJax_SansSerif-Bold.woff
│ │ │ │ │ │ ├── MathJax_SansSerif-Italic.woff
│ │ │ │ │ │ ├── MathJax_SansSerif-Regular.woff
│ │ │ │ │ │ ├── MathJax_Script-Regular.woff
│ │ │ │ │ │ ├── MathJax_Size1-Regular.woff
│ │ │ │ │ │ ├── MathJax_Size2-Regular.woff
│ │ │ │ │ │ ├── MathJax_Size3-Regular.woff
│ │ │ │ │ │ ├── MathJax_Size4-Regular.woff
│ │ │ │ │ │ ├── MathJax_Typewriter-Regular.woff
│ │ │ │ │ │ ├── MathJax_Vector-Bold.woff
│ │ │ │ │ │ ├── MathJax_Vector-Regular.woff
│ │ │ │ │ │ └── MathJax_Zero.woff
│ │ │ │ ├── svg.js
│ │ │ │ └── svg
│ │ │ │ │ └── fonts
│ │ │ │ │ └── tex.js
│ │ │ └── tex-mml-chtml.js
│ │ │ └── latex
│ │ │ ├── doorstop.cls
│ │ │ ├── doorstop.yml
│ │ │ └── logo-black-white.png
│ ├── importer.py
│ ├── item.py
│ ├── publisher.py
│ ├── publishers
│ │ ├── __init__.py
│ │ ├── _latex_functions.py
│ │ ├── base.py
│ │ ├── html.py
│ │ ├── latex.py
│ │ ├── markdown.py
│ │ ├── tests
│ │ │ ├── helpers.py
│ │ │ ├── helpers_latex.py
│ │ │ ├── test_publisher_html.py
│ │ │ ├── test_publisher_html_doc.py
│ │ │ ├── test_publisher_latex.py
│ │ │ ├── test_publisher_latex_doc.py
│ │ │ ├── test_publisher_latex_environments.py
│ │ │ ├── test_publisher_markdown.py
│ │ │ ├── test_publisher_markdown_doc.py
│ │ │ └── test_publisher_text.py
│ │ └── text.py
│ ├── reference_finder.py
│ ├── template.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── docs
│ │ │ ├── .doorstop.yml
│ │ │ ├── LLT001.yml
│ │ │ ├── LLT002.yml
│ │ │ ├── LLT003.yml
│ │ │ ├── LLT004.yml
│ │ │ ├── LLT005.yml
│ │ │ ├── LLT007.yml
│ │ │ ├── LLT008.yml
│ │ │ ├── LLT009.yml
│ │ │ └── LLT010.yml
│ │ ├── files
│ │ │ ├── .doorstop.skip
│ │ │ ├── .doorstop.yml
│ │ │ ├── .venv
│ │ │ │ └── doorstop
│ │ │ │ │ └── reqs
│ │ │ │ │ └── .doorstop.yml
│ │ │ ├── REQ001.yml
│ │ │ ├── REQ002.yml
│ │ │ ├── REQ003.yml
│ │ │ ├── REQ006.yml
│ │ │ ├── REQ2-001.yml
│ │ │ ├── a
│ │ │ │ ├── b
│ │ │ │ │ └── template.yml
│ │ │ │ └── template.yml
│ │ │ ├── child
│ │ │ │ ├── .doorstop.skip
│ │ │ │ ├── .doorstop.yml
│ │ │ │ ├── TST001.yml
│ │ │ │ └── TST002.yml
│ │ │ ├── exported-huge.xlsx
│ │ │ ├── exported-modified.csv
│ │ │ ├── exported.csv
│ │ │ ├── exported.tsv
│ │ │ ├── exported.xlsx
│ │ │ ├── exported.yml
│ │ │ ├── external
│ │ │ │ ├── text.txt
│ │ │ │ └── text2.txt
│ │ │ ├── formula.xlsx
│ │ │ ├── index.html
│ │ │ ├── index.md
│ │ │ ├── index2.html
│ │ │ ├── index2.md
│ │ │ ├── new
│ │ │ │ ├── .doorstop.skip
│ │ │ │ └── .doorstop.yml
│ │ │ ├── parent
│ │ │ │ ├── .doorstop.skip
│ │ │ │ ├── .doorstop.yml
│ │ │ │ ├── SYS001.yml
│ │ │ │ └── SYS002.yml
│ │ │ ├── published.html
│ │ │ ├── published.md
│ │ │ ├── published.txt
│ │ │ ├── published2.html
│ │ │ ├── published2.md
│ │ │ ├── published2.txt
│ │ │ ├── skipped.txt
│ │ │ ├── subfolder
│ │ │ │ ├── REQ004.yml
│ │ │ │ └── REQ005.yml
│ │ │ └── testmatrix.csv
│ │ ├── files_md
│ │ │ ├── .doorstop.skip
│ │ │ ├── .doorstop.yml
│ │ │ ├── REQ001.md
│ │ │ └── REQ002.yml
│ │ ├── helpers.py
│ │ ├── test_all.py
│ │ ├── test_builder.py
│ │ ├── test_common.py
│ │ ├── test_document.py
│ │ ├── test_exporter.py
│ │ ├── test_fixtures
│ │ │ ├── 001-item-references-utf8-keyword
│ │ │ │ ├── REQ-UTF8.yml
│ │ │ │ └── files
│ │ │ │ │ └── text-utf8.txt
│ │ │ └── 002-utf8-characters
│ │ │ │ ├── REQ-CYRILLIC.yml
│ │ │ │ ├── REQ-CYRILLIC_crlf.yml
│ │ │ │ └── REQ-MIT.yml
│ │ ├── test_importer.py
│ │ ├── test_item.py
│ │ ├── test_item_extensions.py
│ │ ├── test_item_validator.py
│ │ ├── test_publisher.py
│ │ ├── test_reference_finder.py
│ │ ├── test_template.py
│ │ ├── test_tree.py
│ │ ├── test_types.py
│ │ ├── test_yaml_validator.py
│ │ └── validators
│ │ │ └── validator_dummy.py
│ ├── tree.py
│ ├── types.py
│ ├── validators
│ │ └── item_validator.py
│ ├── vcs
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── git.py
│ │ ├── mercurial.py
│ │ ├── mockvcs.py
│ │ ├── subversion.py
│ │ └── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_all.py
│ │ │ ├── test_base.py
│ │ │ └── test_commands.py
│ └── yaml_validator.py
├── gui
│ ├── __init__.py
│ ├── application.py
│ ├── main.py
│ ├── resources.py
│ ├── tests
│ │ ├── __init__.py
│ │ └── test_all.py
│ ├── utilTkinter.py
│ └── widget.py
├── server
│ ├── __init__.py
│ ├── client.py
│ ├── main.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_all.py
│ │ ├── test_api.py
│ │ ├── test_client.py
│ │ └── test_server.py
│ └── utilities.py
├── settings.py
├── tests
│ └── test_init.py
└── views
│ ├── base.tpl
│ ├── document_list.tpl
│ ├── doorstop.tpl
│ └── item_list.tpl
├── git_hooks
└── check_unreviewed_requirements.sh
├── mkdocs.yml
├── poetry.lock
├── pyproject.toml
├── pytest.log
├── reqs
├── .doorstop.yml
├── REQ001.yml
├── REQ002.yml
├── REQ003.yml
├── REQ004.yml
├── REQ006.yml
├── REQ007.yml
├── REQ008.yml
├── REQ009.yml
├── REQ010.yml
├── REQ011.yml
├── REQ012.yml
├── REQ013.yml
├── REQ014.yml
├── REQ015.yml
├── REQ016.yml
├── REQ017.yml
├── REQ018.yml
├── REQ019.yml
├── assets
│ └── logo-black-white.png
├── ext
│ ├── .doorstop.yml
│ ├── .req_sha_item_validator.py
│ ├── EXT001.yml
│ ├── EXT002.yml
│ ├── test-modified.file
│ └── test.file
└── tutorial
│ ├── .doorstop.yml
│ ├── TUT001.yml
│ ├── TUT002.yml
│ ├── TUT003.yml
│ ├── TUT004.yml
│ ├── TUT005.yml
│ ├── TUT008.yml
│ ├── TUT009.yml
│ ├── TUT010.yml
│ ├── TUT011.yml
│ ├── TUT012.yml
│ ├── TUT013.yml
│ ├── TUT014.yml
│ ├── TUT015.yml
│ ├── TUT016.yml
│ ├── TUT017.yml
│ ├── TUT018.yml
│ ├── TUT019.yml
│ ├── TUT020.yml
│ ├── TUT021.yml
│ ├── TUT022.yml
│ ├── TUT023.yml
│ ├── TUT024.yml
│ └── TUT025.yml
└── scent.py
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 |
3 | branch = true
4 |
5 | omit =
6 | .venv/*
7 | */tests/*
8 | */__main__.py
9 |
10 | dynamic_context = test_function
11 |
12 | [report]
13 |
14 | omit =
15 | */gui/main.py
16 | */gui/application.py
17 | */gui/utilTkinter.py
18 | */gui/widget.py
19 |
20 | exclude_lines =
21 | pragma: no cover
22 | raise NotImplementedError
23 | except DistributionNotFound
24 |
25 | skip_covered = false
26 |
27 | [html]
28 | show_contexts = True
29 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.bat]
12 | end_of_line = crlf
13 |
14 | [*.md]
15 | trim_trailing_whitespace = false
16 |
17 | [*.py]
18 | indent_style = space
19 | indent_size = 4
20 |
21 | [*.yml]
22 | indent_style = space
23 |
24 | [Makefile]
25 | indent_style = tab
26 | indent_size = 4
27 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | CHANGELOG.md merge=union
3 |
4 | *.xlsx binary
5 | *.min.js binary
6 | *.min.css binary
7 | doorstop/core/files/templates/html/output/*.js binary
8 | doorstop/core/files/templates/html/tex-mml-chtml.js binary
9 | doorstop/core/tests/files/published*.html binary
10 |
11 | # Python, Git, and Cygwin have different ideas about the correct line endings
12 | *.yml -text
13 | *.txt -text
14 | *.md -text
15 | *.html -text
16 | *.csv -text
17 | *.tsv -text
18 |
--------------------------------------------------------------------------------
/.github/workflows/change-coverage.yml:
--------------------------------------------------------------------------------
1 | name: Execute tests
2 | on:
3 | workflow_call:
4 | inputs:
5 | basepath:
6 | required: false
7 | type: string
8 | os:
9 | required: true
10 | type: string
11 | workpath:
12 | required: true
13 | type: string
14 |
15 | jobs:
16 | change-coverage:
17 | runs-on: ubuntu-latest
18 | name: Change coverage
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v4
22 | with:
23 | fetch-depth: 0
24 |
25 | - uses: actions/setup-python@v5
26 | with:
27 | python-version: "3.11"
28 | architecture: x64
29 |
30 | - uses: Gr1N/setup-poetry@v9
31 |
32 | - name: Check system dependencies
33 | run: make doctor
34 |
35 | - uses: actions/cache@v4
36 | with:
37 | path: .venv
38 | key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }}
39 |
40 | - name: Install project dependencies
41 | run: make install
42 |
43 | - name: Check coverage
44 | run: |
45 | TEST_INTEGRATION=true poetry run pytest doorstop --doctest-modules --cov=doorstop --cov-report=xml --cov-report=term-missing
46 | git fetch origin develop:develop
47 | # TEST_INTEGRATION=true poetry run diff-cover ./coverage.xml --fail-under=100 --compare-branch=develop
48 | TEST_INTEGRATION=true poetry run diff-cover ./coverage.xml --fail-under=100 --compare-branch=$(git for-each-ref --sort=-committerdate refs/heads/develop | cut -f 1 -d ' ')
49 |
--------------------------------------------------------------------------------
/.github/workflows/execute-tests.yml:
--------------------------------------------------------------------------------
1 | name: Execute tests
2 | on:
3 | workflow_call:
4 | inputs:
5 | basepath:
6 | required: false
7 | type: string
8 | os:
9 | required: true
10 | type: string
11 | workpath:
12 | required: true
13 | type: string
14 | secrets:
15 | CODECOV_TOKEN:
16 | required: true
17 |
18 | jobs:
19 | test:
20 | runs-on: ${{ inputs.os }}
21 | strategy:
22 | matrix:
23 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
24 | name: Python ${{ matrix.python-version }}
25 |
26 | defaults:
27 | run:
28 | working-directory: ${{ inputs.workpath }}
29 |
30 | steps:
31 | - name: Checkout
32 | uses: actions/checkout@v4
33 |
34 | - name: Change path on Windows
35 | if: ${{ inputs.os == 'windows-latest' }}
36 | # Cannot start powershell from a path that does not exist, so change
37 | # working directory for this step only.
38 | working-directory: ${{ inputs.basepath }}
39 | run: |
40 | mkdir -p ${{ inputs.workpath }}
41 | mv $env:GITHUB_WORKSPACE\* ${{ inputs.workpath }}\ -Force
42 |
43 | - uses: actions/setup-python@v5
44 | with:
45 | python-version: ${{ matrix.python-version }}
46 | architecture: x64
47 |
48 | - uses: Gr1N/setup-poetry@v9
49 |
50 | - name: Check system dependencies
51 | run: make doctor
52 |
53 | - uses: actions/cache@v4
54 | with:
55 | path: .venv
56 | key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }}
57 |
58 | - name: Install project dependencies
59 | run: make install
60 |
61 | - name: Run tests
62 | run: make test
63 |
64 | - name: Upload coverage
65 | uses: codecov/codecov-action@v4
66 | if: ${{ inputs.os == 'ubuntu-latest' && matrix.python-version == '3.9' && (github.event_name == 'push' && !startsWith(github.ref, 'refs/heads/dependabot/') || (github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork)) }}
67 | with:
68 | token: ${{ secrets.CODECOV_TOKEN }}
69 | fail_ci_if_error: true
70 |
71 | - name: Run checks
72 | run: make check
73 | if: ${{ inputs.os == 'ubuntu-latest' }}
74 |
75 | - name: Run demo
76 | run: make demo
77 | if: ${{ inputs.os == 'ubuntu-latest' }}
78 |
--------------------------------------------------------------------------------
/.github/workflows/test-linux.yml:
--------------------------------------------------------------------------------
1 | name: Linux
2 |
3 | on:
4 | push:
5 | pull_request:
6 | branches: [ develop ]
7 |
8 | jobs:
9 | Coverage:
10 | uses: ./.github/workflows/change-coverage.yml
11 | with:
12 | os: "ubuntu-latest"
13 | workpath: "/home/runner/work/doorstop/doorstop"
14 | if: github.event_name == 'pull_request'
15 |
16 | Test:
17 | uses: ./.github/workflows/execute-tests.yml
18 | with:
19 | os: "ubuntu-latest"
20 | workpath: "/home/runner/work/doorstop/doorstop"
21 | secrets:
22 | CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}
23 |
--------------------------------------------------------------------------------
/.github/workflows/test-osx.yml:
--------------------------------------------------------------------------------
1 | name: macOS
2 |
3 | on:
4 | push:
5 | pull_request:
6 | branches: [ develop ]
7 |
8 | jobs:
9 | Test:
10 | uses: ./.github/workflows/execute-tests.yml
11 | with:
12 | os: "macos-13"
13 | workpath: "/Users/runner/work/doorstop/doorstop"
14 | secrets:
15 | CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}
16 |
--------------------------------------------------------------------------------
/.github/workflows/test-windows.yml:
--------------------------------------------------------------------------------
1 | name: Windows
2 |
3 | on:
4 | push:
5 | pull_request:
6 | branches: [ develop ]
7 |
8 | jobs:
9 | Test:
10 | uses: ./.github/workflows/execute-tests.yml
11 | with:
12 | basepath: 'D:\'
13 | os: "windows-latest"
14 | workpath: 'C:\a\doorstop\doorstop'
15 | secrets:
16 | CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Temporary Python files
2 | *.pyc
3 | *.egg-info
4 | __pycache__
5 | .ipynb_checkpoints
6 | setup.py
7 |
8 | # Temporary OS files
9 | Icon*
10 |
11 | # Temporary virtual environment files
12 | /.cache/
13 | /.venv/
14 | /.python-version
15 |
16 | # Temporary server files
17 | .env
18 | *.pid
19 |
20 | # Generated documentation
21 | /docs/gen/
22 | /docs/apidocs/
23 | /site/
24 | /*.html
25 | /docs/*.png
26 | texput.log
27 |
28 | # Google Drive
29 | *.gdoc
30 | *.gsheet
31 | *.gslides
32 | *.gdraw
33 |
34 | # Testing and coverage results
35 | /.coverage
36 | /.coverage.*
37 | /htmlcov/
38 | /xmlreport/
39 | /pyunit.xml
40 | /tmp/
41 | *.tmp
42 | coverage.xml
43 |
44 | # Build and release directories
45 | /build/
46 | /dist/
47 | *.spec
48 | **/MathJax
49 |
50 | # Sublime Text
51 | *.sublime-workspace
52 |
53 | # Eclipse
54 | .settings
55 |
--------------------------------------------------------------------------------
/.mypy.ini:
--------------------------------------------------------------------------------
1 | [mypy]
2 |
3 | ignore_missing_imports = true
4 | no_implicit_optional = true
5 | check_untyped_defs = true
6 |
7 | cache_dir = .cache/mypy/
8 |
9 | [mypy-doorstop.*.tests.*]
10 |
11 | ignore_errors = True
12 |
--------------------------------------------------------------------------------
/.noserc:
--------------------------------------------------------------------------------
1 | [nosetests]
2 |
3 | exe=1
4 |
5 | with-doctest=1
6 |
7 | with-coverage=1
8 | cover-package=doorstop.common,doorstop.core,
9 | doorstop.cli.main,doorstop.cli.utilities,
10 | doorstop.gui,doorstop.server
11 | cover-erase=1
12 |
13 | verbosity=1
14 | logging-level=DEBUG
15 | logging-format=[%(levelname)-8s] %(message)s
16 |
--------------------------------------------------------------------------------
/.pre-commit-hooks.yaml:
--------------------------------------------------------------------------------
1 | # expected stages: commit, commit-msg, manual, merge-commit, post-checkout, post-commit, post-merge, post-rewrite, prepare-commit-msg, push
2 |
3 | - id: check-doorstop-errors
4 | args: ["-W"]
5 | name: check for doorstop errors
6 | description: ensures the changes introduces no errors
7 | entry: doorstop
8 | language: python
9 | verbose: true
10 | pass_filenames: false
11 | stages: [commit, push, manual]
12 |
13 | - id: check-unreviewed-items
14 | name: ensure that all requirements are reviewed before being committed
15 | description: ensure that all documents/requirements are reviewed before being committed
16 | entry: git_hooks/check_unreviewed_requirements.sh
17 | language: script
18 | verbose: true
19 | pass_filenames: false
20 | stages: [merge-commit, manual]
21 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Doorstop
4 |
5 |
6 |
7 |
8 |
9 | org.python.pydev.PyDevBuilder
10 |
11 |
12 |
13 |
14 |
15 | org.python.pydev.pythonNature
16 |
17 |
18 |
19 | 1398478527151
20 |
21 | 26
22 |
23 | org.eclipse.ui.ide.multiFilter
24 | 1.0-name-matches-false-false-__pycache__
25 |
26 |
27 |
28 | 1398478527152
29 |
30 | 26
31 |
32 | org.eclipse.ui.ide.multiFilter
33 | 1.0-name-matches-false-false-*.egg-info
34 |
35 |
36 |
37 | 1398478527153
38 |
39 | 26
40 |
41 | org.eclipse.ui.ide.multiFilter
42 | 1.0-name-matches-false-false-env
43 |
44 |
45 |
46 | 1398478527154
47 |
48 | 22
49 |
50 | org.eclipse.ui.ide.multiFilter
51 | 1.0-name-matches-false-false-README.rst
52 |
53 |
54 |
55 | 1398478527155
56 |
57 | 26
58 |
59 | org.eclipse.ui.ide.multiFilter
60 | 1.0-name-matches-false-false-apidocs
61 |
62 |
63 |
64 | 1398478527156
65 |
66 | 22
67 |
68 | org.eclipse.ui.ide.multiFilter
69 | 1.0-name-matches-false-false-*.sublime-*
70 |
71 |
72 |
73 | 1398478527157
74 |
75 | 22
76 |
77 | org.eclipse.ui.ide.multiFilter
78 | 1.0-name-matches-false-false-README*.html
79 |
80 |
81 |
82 | 1398478527158
83 |
84 | 26
85 |
86 | org.eclipse.ui.ide.multiFilter
87 | 1.0-name-matches-false-false-build
88 |
89 |
90 |
91 | 1398478527159
92 |
93 | 26
94 |
95 | org.eclipse.ui.ide.multiFilter
96 | 1.0-name-matches-false-false-dist
97 |
98 |
99 |
100 | 1429034866152
101 |
102 | 26
103 |
104 | org.eclipse.ui.ide.multiFilter
105 | 1.0-name-matches-false-false-htmlcov
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/.pydevproject:
--------------------------------------------------------------------------------
1 |
2 |
3 | python 3.0
4 | Doorstop
5 |
6 | /${PROJECT_DIR_NAME}
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.pydocstyle.ini:
--------------------------------------------------------------------------------
1 | [pydocstyle]
2 |
3 | # D211: No blank lines allowed before class docstring
4 | add_select = D211
5 |
6 | # D100: Missing docstring in public module
7 | # D101: Missing docstring in public class
8 | # D102: Missing docstring in public method
9 | # D103: Missing docstring in public function
10 | # D104: Missing docstring in public package
11 | # D105: Missing docstring in magic method
12 | # D107: Missing docstring in __init__
13 | # D202: No blank lines allowed after function docstring
14 | add_ignore = D100,D101,D102,D103,D104,D105,D107,D202
15 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | build:
4 | os: "ubuntu-22.04"
5 | tools:
6 | python: "3.9"
7 | jobs:
8 | post_create_environment:
9 | # Install poetry
10 | # https://python-poetry.org/docs/#installing-manually
11 | - pip install poetry
12 | post_install:
13 | # Install dependencies with 'docs' dependency group
14 | # https://python-poetry.org/docs/managing-dependencies/#dependency-groups
15 | # VIRTUAL_ENV needs to be set manually for now.
16 | # See https://github.com/readthedocs/readthedocs.org/pull/11152/
17 | - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install
18 |
19 | mkdocs:
20 | configuration: mkdocs.yml
21 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | build:
2 | tests:
3 | override:
4 | - pylint-run --rcfile=.pylint.ini
5 | - py-scrutinizer-run
6 | checks:
7 | python:
8 | code_rating: true
9 | duplicate_code: true
10 | filter:
11 | excluded_paths:
12 | - "*/tests/*"
13 |
--------------------------------------------------------------------------------
/.tool-versions:
--------------------------------------------------------------------------------
1 | python 3.13.3
2 | poetry 2.1.1
3 |
--------------------------------------------------------------------------------
/.verchew.ini:
--------------------------------------------------------------------------------
1 | [Make]
2 |
3 | cli = make
4 | version = GNU Make
5 |
6 | [Python]
7 |
8 | cli = python
9 | version = 3.9 || 3.10 || 3.11 || 3.12 || 3.13
10 |
11 | [Poetry]
12 |
13 | cli = poetry
14 | version = 2
15 |
16 | [Graphviz]
17 |
18 | cli = dot
19 | cli_version_arg = -V
20 | version = 9 || 10 || 11 || 12
21 | optional = true
22 | message = This is only needed to generate UML diagrams for documentation.
23 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.exclude": {
3 | ".cache/": true,
4 | ".venv/": true,
5 | "*.egg-info": true,
6 | "pip-wheel-metadata/": true,
7 | "**/__pycache__": true,
8 | "**/*.pyc": true,
9 | "**/.ipynb_checkpoints": true,
10 | "**/tmp/": true,
11 | "dist/": true,
12 | "htmlcov/": true,
13 | "prof/": true,
14 | "site/": true,
15 | },
16 | "python.defaultInterpreterPath": ".venv/bin/python",
17 | "pylint.args": ["--rcfile=.pylint.ini"],
18 | "editor.formatOnSave": true,
19 | "cSpell.words": [
20 | "Autobuild",
21 | "basepath",
22 | "cget",
23 | "choco",
24 | "clevel",
25 | "cname",
26 | "codecov",
27 | "codeql",
28 | "cors",
29 | "coveragespace",
30 | "curr",
31 | "cust",
32 | "doorhole",
33 | "dunder",
34 | "endfirsthead",
35 | "endfoot",
36 | "endhead",
37 | "endlastfoot",
38 | "enduml",
39 | "evalue",
40 | "EXTS",
41 | "frontmatter",
42 | "Gitter",
43 | "graphviz",
44 | "HLINE",
45 | "hyperref",
46 | "isort",
47 | "itered",
48 | "lgpl",
49 | "LINESEPERATOR",
50 | "linkify",
51 | "LONGTABLE",
52 | "macfsevents",
53 | "mkdir",
54 | "mkdocs",
55 | "mockvcs",
56 | "mylevel",
57 | "mypy",
58 | "nlev",
59 | "nlevel",
60 | "nuid",
61 | "nums",
62 | "openpyxl",
63 | "pids",
64 | "plantuml",
65 | "plev",
66 | "plevel",
67 | "puid",
68 | "pydocstyle",
69 | "pyficache",
70 | "pygments",
71 | "pyinstaller",
72 | "pync",
73 | "pyyaml",
74 | "quickstart",
75 | "sargs",
76 | "setuptools",
77 | "sgignores",
78 | "spdx",
79 | "textit",
80 | "textwidth",
81 | "tkinter",
82 | "UID's",
83 | "uids",
84 | "unreviewed",
85 | "USERPROFILE",
86 | "venv",
87 | "vvignores",
88 | "winfo",
89 | "workpath",
90 | "xlsx",
91 | "yamlfile"
92 | ]
93 | }
94 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Setup
2 |
3 | ## Requirements
4 |
5 | * Make:
6 | * macOS: `$ xcode-select --install`
7 | * Linux: [https://www.gnu.org/software/make](https://www.gnu.org/software/make)
8 | * Windows: [https://mingw.org/download/installer](https://mingw.org/download/installer)
9 | * Python: `$ pyenv install`
10 | * Poetry: [https://python-poetry.org/docs/#installation](https://python-poetry.org/docs/#installation)
11 | * Graphviz:
12 | * macOS: `$ brew install graphviz`
13 | * Linux: [https://graphviz.org/download](https://graphviz.org/download/)
14 | * Windows: [https://graphviz.org/download](https://graphviz.org/download/)
15 |
16 | To confirm these system dependencies are configured correctly:
17 |
18 | ```sh
19 | $ make doctor
20 | ```
21 |
22 | ## Installation
23 |
24 | Install project dependencies into a virtual environment:
25 |
26 | ```sh
27 | $ make install
28 | ```
29 |
30 | # Development Tasks
31 |
32 | ## Manual
33 |
34 | Run the tests:
35 |
36 | ```sh
37 | $ make test
38 | ```
39 |
40 | Run static analysis:
41 |
42 | ```sh
43 | $ make check
44 | ```
45 |
46 | Build the documentation:
47 |
48 | ```sh
49 | $ make docs
50 | ```
51 |
52 | Local install for external testing:
53 |
54 | ```sh
55 | $ make dev-install
56 | ```
57 |
58 | Clean everything:
59 | ```sh
60 | $ make clean
61 | ```
62 |
63 | Compare coverage to current `develop` branch to see if changes causes reduced coverage.
64 | Please run before creating a PR.
65 | ```sh
66 | $ make test-cover
67 | ```
68 |
69 | ## Automatic
70 |
71 | Keep all of the above tasks running on change:
72 |
73 | ```sh
74 | $ make dev
75 | ```
76 |
77 | > In order to have OS X notifications, `brew install terminal-notifier`.
78 |
79 | # Continuous Integration
80 |
81 | The CI server will report overall build status:
82 |
83 | ```sh
84 | $ make ci
85 | ```
86 |
87 | # Release Tasks
88 |
89 | Release to PyPI:
90 |
91 | ```sh
92 | $ make upload
93 | ```
94 |
--------------------------------------------------------------------------------
/Doorstop.sublime-project:
--------------------------------------------------------------------------------
1 | {
2 | "folders":
3 | [
4 | {
5 | "path": ".",
6 | "file_exclude_patterns": [".coverage", "*.sublime-workspace"],
7 | "folder_exclude_patterns": [".*", "__pycache__", "build", "dist", "env", "*.egg-info"]
8 | }
9 | ],
10 | "settings":
11 | {
12 | "tab_size": 4,
13 | "translate_tabs_to_spaces": true
14 | },
15 | "SublimeLinter":
16 | {
17 | "linters":
18 | {
19 | "pep257": {
20 | "@disable": false,
21 | "args": [],
22 | "excludes": []
23 | },
24 | "pep8": {
25 | "@disable": false,
26 | "args": [],
27 | "excludes": [],
28 | "ignore": "E501",
29 | "max-line-length": 79,
30 | "select": ""
31 | },
32 | "pylint": {
33 | "@disable": false,
34 | "rcfile": ".pylint.ini"
35 | }
36 | }
37 | },
38 | }
39 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.rst *.txt *.md
2 | recursive-include docs *.rst *.txt *.md
3 | graft */files
4 | graft */*/files
5 | graft doorstop/core/files/assets
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: sappy public --port=${PORT}
2 |
--------------------------------------------------------------------------------
/bin/checksum:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import hashlib
5 | import sys
6 |
7 |
8 | def run(paths):
9 | hash_md5 = hashlib.md5()
10 |
11 | for path in paths:
12 | try:
13 | with open(path, 'rb') as f:
14 | for chunk in iter(lambda: f.read(4096), b''):
15 | hash_md5.update(chunk)
16 | except IOError:
17 | hash_md5.update(path.encode())
18 |
19 | print(hash_md5.hexdigest())
20 |
21 |
22 | if __name__ == '__main__':
23 | run(sys.argv[1:])
24 |
--------------------------------------------------------------------------------
/bin/example-adapter.wsgi:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Example adapter.wsgi for doorstop.
4 |
5 | import os
6 | import sys
7 | import bottle
8 |
9 | app = None
10 |
11 | def application(environ, start_response):
12 | global app
13 | if not app:
14 | doorstop_path = "/opt/doorstop"
15 | os.chdir(os.path.dirname(__file__))
16 |
17 | project_path = environ['DOORSTOP_PROJECT_DIR']
18 | baseurl = environ['DOORSTOP_BASE_URL']
19 |
20 | parameters = [
21 | '--project', project_path,
22 | '--baseurl', baseurl
23 | '--wsgi'
24 | ]
25 |
26 | sys.path.append(doorstop_path)
27 | from doorstop.server.main import main as servermain
28 |
29 | servermain(parameters)
30 |
31 | app = bottle.default_app()
32 |
33 | return app(environ, start_response)
34 |
35 |
--------------------------------------------------------------------------------
/bin/open:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import os
5 | import sys
6 |
7 |
8 | COMMANDS = {
9 | 'linux': "open",
10 | 'win32': "cmd /c start",
11 | 'cygwin': "cygstart",
12 | 'darwin': "open",
13 | }
14 |
15 |
16 | def run(path):
17 | command = COMMANDS.get(sys.platform, "open")
18 | os.system(command + ' ' + path)
19 |
20 |
21 | if __name__ == '__main__':
22 | run(sys.argv[-1])
23 |
--------------------------------------------------------------------------------
/bin/post_compile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eo pipefail
4 |
5 | indent() {
6 | sed "s/^/ /"
7 | }
8 |
9 | puts-step() {
10 | echo "-----> $@"
11 | }
12 |
13 | puts-step "Installing dependencies with poetry"
14 | poetry install | indent
15 |
16 | puts-step "Generating HTML from requirements"
17 | touch .mockvcs
18 | poetry run doorstop publish all public | indent
19 |
--------------------------------------------------------------------------------
/docs/about/changelog.md:
--------------------------------------------------------------------------------
1 | ../../CHANGELOG.md
--------------------------------------------------------------------------------
/docs/about/contributing.md:
--------------------------------------------------------------------------------
1 | ../../CONTRIBUTING.md
--------------------------------------------------------------------------------
/docs/about/license.md:
--------------------------------------------------------------------------------
1 | ../../LICENSE.md
--------------------------------------------------------------------------------
/docs/api/scripting.md:
--------------------------------------------------------------------------------
1 |
Scripting Interface
2 |
3 | Being written in Python, Doorstop allows you to leverage the full power of Python to write scripts to manipulate requirements, run custom queries across all documents, and even inject your own validation rules.
4 |
5 | # REPL
6 |
7 | For ad hoc introspection, let Doorstop build your tree of documents in your preferred Python [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) or notebook session:
8 |
9 | ```python
10 | >>> import doorstop
11 | >>> tree = doorstop.build()
12 | >>> tree
13 |
14 | >>> len(tree.documents)
15 | 4
16 | >>> document = tree.find_document('REQ')
17 | >>> document
18 | Document('/Users/Browning/Documents/doorstop/reqs')
19 | >>> sum(1 for item in document if item.active)
20 | 18
21 | ```
22 |
23 | # Generic Scripting
24 |
25 | For reusable workflows, create a Python script that acts on your tree of documents:
26 |
27 | ```python
28 | #!/usr/bin/env python
29 |
30 | import doorstop
31 |
32 | tree = doorstop.build()
33 | document = tree.find_document('REQ')
34 | count = sum(1 for item in document if item.active)
35 |
36 | print(f"{count} active items in {document}")
37 | ```
38 |
39 | # Validation Hooks
40 |
41 | To extend the default set of [validations](../cli/validation.md) that can be performed, Doorstop provides a "hook" mechanism to simplify scripts that need to operate on multiple documents or items.
42 |
43 | For this use case, create a script to call in place of the default command-line interface:
44 |
45 | ```python
46 | #!/usr/bin/env python
47 |
48 | import sys
49 | from doorstop import build, DoorstopInfo, DoorstopWarning, DoorstopError
50 |
51 |
52 | def main():
53 | tree = build()
54 | success = tree.validate(document_hook=check_document, item_hook=check_item)
55 | sys.exit(0 if success else 1)
56 |
57 |
58 | def check_document(document, tree):
59 | if sum(1 for i in document if i.normative) < 10:
60 | yield DoorstopInfo("fewer than 10 normative items")
61 |
62 |
63 | def check_item(item, document, tree):
64 | if not item.get('type'):
65 | yield DoorstopWarning("no type specified")
66 | if item.derived and not item.get('rationale'):
67 | yield DoorstopError("derived but no rationale")
68 |
69 |
70 | if __name__ == '__main__':
71 | main()
72 | ```
73 |
74 | Both `document_hook` and `item_hook` are optional, but if provided these callbacks will be passed each corresponding instance. Each callback should yield instances of Doorstop's exception classes based on severity of the issue.
75 |
76 | ## Validation Hook per folder
77 |
78 | Doorstop also has an extension which allows creating an item validation per folder, allowing the document to have different validations for each document section.
79 | To enable this mechanism you must insert into your `.doorstop.yml` file the following lines:
80 |
81 | ```yaml
82 | extensions:
83 | item_validator: .req_sha_item_validator.py # a python file path relative to .doorstop.yml
84 | ```
85 |
86 | or
87 |
88 | ```yaml
89 | extensions:
90 | item_validator: ../validators/my_complex_validator.py # a python file path relative to .doorstop.yml
91 | ```
92 |
93 | The referenced file must have a function called `item_validator` with a single parameter `item`.
94 |
95 | Example:
96 |
97 | ```python
98 |
99 |
100 | def item_validator(item):
101 | if getattr(item, "references") == None:
102 | return [] # early return
103 | for ref in item.references:
104 | if ref['sha'] != item._hash_reference(ref['path']):
105 | yield DoorstopError("Hash has changed and it was not reviewed properly")
106 |
107 | ```
108 |
109 | Although it is not required, it is recommended to yield a Doorstop type such as,
110 | `DoorstopInfo`, `DoorstopError`, or `DoorstopWarning`.
111 |
--------------------------------------------------------------------------------
/docs/assets/logo.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/docs/assets/logo.xcf
--------------------------------------------------------------------------------
/docs/cli/interchange.md:
--------------------------------------------------------------------------------
1 | # Exporting Requirements
2 |
3 | Documents can be exported for editing or to exchange with other systems:
4 |
5 | ```sh
6 | $ doorstop export TST
7 | TST001:
8 | active: true
9 | dervied: false
10 | level: 1
11 | links:
12 | - REQ001
13 | normative: true
14 | ref: ''
15 | text: |
16 | Verify the foobar will foo and bar.
17 | ```
18 |
19 | Or a file can be created using one of the supported extensions:
20 |
21 | ```sh
22 | $ doorstop export TST path/to/tst.csv
23 | exporting TST to path/to/tst.csv...
24 | exported: path/to/tst.csv
25 | ```
26 |
27 | Supported formats:
28 |
29 | - YAML: `.yml`
30 | - Comma-Separated Values: `.csv`
31 | - Tab-Separated Values: `.tsv`
32 | - Microsoft Office Excel: `.xlsx`
33 |
34 | # Importing Requirements
35 |
36 | Items can be created/updated from the export formats:
37 |
38 | ```sh
39 | $ doorstop import path/to/tst.csv TST
40 | ```
41 |
--------------------------------------------------------------------------------
/docs/cli/reordering.md:
--------------------------------------------------------------------------------
1 | # Documents Headings
2 |
3 | The items in a document are arranged according to their level attribute, with levels ending in .0
4 | creating headings and the subsequent items forming sub headings.
5 |
6 | A document can contain an arbitraty number of headings and sub headings.
7 |
8 | A normative heading item will display the item's uid as the heading text. A non normative heading
9 | displays the first line of the item text, with any subsequent text displayed in the body of the heading.
10 |
11 |
12 | # Automatic item reordering
13 |
14 | Items in a document can be automatically reordered to remove gaps or duplicate entries in item headings.
15 |
16 | ```sh
17 | $ doorstop reorder --auto REQ
18 | building tree...
19 | reordering document REQ...
20 | reordered document: REQ
21 | ```
22 |
23 | # Manual item reordering
24 |
25 | Manual reordering creates an index.yml file in the document directory which describes the desired outline
26 | of the document. The index.yml is edited, changing the indentation and order of the items to update the
27 | levels of each item.
28 |
29 | ```sh
30 | $ doorstop reorder --tool vi REQ
31 | building tree...
32 | reorder from 'reqs/index.yml'? [y/n] y
33 | reordering document REQ...
34 | reordered document: REQ
35 | ```
36 |
37 | ## Adding a new item
38 |
39 | An item can be added by adding a new line to the index.yml containing an unknown UID, e.g. new.
40 | A comment following the new item ID can be used to set the item text.
41 | If the line after a new item is further indented the item is treated as a heading and marked
42 | as non normative.
43 |
44 | ```yaml
45 | ###############################################################################
46 | # THIS TEMPORARY FILE WILL BE DELETED AFTER DOCUMENT REORDERING
47 | # MANUALLY INDENT, DEDENT, & MOVE ITEMS TO THEIR DESIRED LEVEL
48 | # A NEW ITEM WILL BE ADDED FOR ANY UNKNOWN IDS, i.e. - new:
49 | # THE COMMENT WILL BE USED AS THE ITEM TEXT FOR NEW ITEMS
50 | # CHANGES WILL BE REFLECTED IN THE ITEM FILES AFTER CONFIRMATION
51 | ###############################################################################
52 |
53 | initial: 1.0
54 | outline:
55 | - REQ018: # Overview
56 | - REQ019: # Doorstop is a requirements management tool that leverage...
57 | - NEW: # The text of a new item
58 | - NEW: # A new heading
59 | - NEW: # The text of a new ite,
60 | ```
61 |
62 | ## Deleting an item
63 |
64 | Deleting a line from index.yml will result in the item being deleted.
65 |
--------------------------------------------------------------------------------
/docs/cli/validation.md:
--------------------------------------------------------------------------------
1 | # Integrity Checks
2 |
3 | To check a document hierarchy for consistency, run the main command:
4 |
5 | ```sh
6 | $ doorstop
7 | building tree...
8 | loading documents...
9 | validating items...
10 |
11 | REQ
12 | │
13 | ├── TUT
14 | │ │
15 | │ └── HLT
16 | │
17 | └── LLT
18 | ```
19 |
20 | By default, the validation run displays messages with a `WARNING` and `ERROR` level.
21 | In case verbose output is enabled, also messages with an `INFO` level
22 | are shown.
23 |
24 | `INFO` level messages are generated under the following conditions:
25 |
26 | * Skipped levels within the items of a document.
27 | * No initial review done for an item.
28 | * The prefix of an UID is not equal to the document prefix.
29 | * The prefix of a link UID is not equal to the parent document prefix.
30 |
31 | `WARNING` level messages are generated under the following conditions:
32 |
33 | * A document contains no items.
34 | * Duplicated levels within the items of a document.
35 | * An item has an empty text attribute.
36 | * An item has unreviewed changes.
37 | * An item's child link is an inactive item.
38 | * An item is linked to a non-normative item.
39 | * An item is linked to itself.
40 | * An item has a suspect linked to an item those fingerprint is not equal to the
41 | one recorded in the link.
42 | * An item in a document with child documents has no links from an item in one of its child documents.
43 | * A normative, non-derived item in a child document has no links.
44 | * A non-normative items has links.
45 | * There is a cycle of item links.
46 |
47 | `ERROR` level messages are generated under the following conditions:
48 |
49 | * An item's parent link is an inactive item.
50 | * An item's link is an invalid or unknown UID.
51 | * An external reference cannot be found.
52 |
53 | ## Links
54 |
55 | To confirm that every item in a document links to its parents:
56 |
57 | ```sh
58 | $ doorstop --strict-child-check
59 | building tree...
60 | loading documents...
61 | validating items...
62 | WARNING: REQ: REQ001: no links from document: TUT
63 | WARNING: REQ: REQ016: no links from document: LLT
64 | WARNING: REQ: REQ017: no links from document: LLT
65 | WARNING: REQ: REQ008: no links from document: TUT
66 | WARNING: REQ: REQ009: no links from document: TUT
67 | WARNING: REQ: REQ014: no links from document: TUT
68 | WARNING: REQ: REQ015: no links from document: TUT
69 |
70 | REQ
71 | │
72 | ├── TUT
73 | │ │
74 | │ └── HLT
75 | │
76 | └── LLT
77 | ```
78 |
79 | ## Clear Suspect Links
80 |
81 | Each link consists of the parent item UID and the
82 | [fingerprint](../reference/item.md#reviewed) of the parent item. When the
83 | fingerprint of a parent item changes, the link is reported as suspect during
84 | validation.
85 |
86 | ```sh
87 | doorstop
88 | building tree...
89 | loading documents...
90 | validating items...
91 | WARNING: LLT: LLT005: suspect link: REQ001
92 |
93 | REQ
94 | │
95 | ├── TUT
96 | │ │
97 | │ └── HLT
98 | │
99 | └── LLT
100 | ```
101 |
102 | You can clear suspect links with the `doorstop clear` command.
103 |
104 | ```sh
105 | $ doorstop clear LLT005
106 | building tree...
107 | clearing item LLT005's suspect links...
108 | ```
109 |
110 | Optionally, you can clear only suspect links to specific parent items.
111 |
112 | ```sh
113 | $ doorstop clear LLT005 REQ002 REQ003
114 | building tree...
115 | clearing item LLT005's suspect links to REQ002, REQ003...
116 | ```
117 |
--------------------------------------------------------------------------------
/docs/examples.md:
--------------------------------------------------------------------------------
1 | # Advanced Setup
2 |
3 | Running `doorstop` in Docker: [tlwt/doorstop-docker](https://github.com/tlwt/doorstop-docker)
4 |
5 | # Sample Projects
6 |
7 | [(add your open source project here)](https://github.com/doorstop-dev/doorstop/edit/develop/docs/examples.md)
8 |
9 | - **[ros-safety/requirements-playground](https://github.com/ros-safety/requirements-playground)**: This is a small repo for demonstrating how Doorstop can be used to manage requirements in a C++ project.
10 | - **[urbasus/literate-programming-in-doorstop](https://github.com/urbasus/literate-programming-in-doorstop)**: Proof-of-concept for combining Doorstop and Literate Programming.
11 |
12 | # 3rd-Party Clients
13 |
14 | - **[doorframe](https://doorframe.io/)**: This is (closed source) GitHub App that uses Doorstop as backend.
15 | - **[sevendays/doorhole](https://github.com/sevendays/doorhole)**: This is standalone GUI for Doorstop.
16 | - **[ownbee/doorstop-edit](https://github.com/ownbee/doorstop-edit)**: Cross-platform Doorstop GUI editor.
17 |
--------------------------------------------------------------------------------
/docs/generate.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from doorstop.core import publisher, builder
4 | from doorstop.cli import utilities
5 |
6 |
7 | def on_pre_build(config):
8 | cwd = os.getcwd()
9 | path = os.path.abspath(os.path.join(cwd, "docs/gen"))
10 | tree = builder.build(cwd=cwd)
11 |
12 | published_path = publisher.publish(tree, path, ".md", index=True)
13 |
14 | if published_path:
15 | utilities.show("published: {}".format(published_path))
16 |
--------------------------------------------------------------------------------
/docs/getting-started/installation.md:
--------------------------------------------------------------------------------
1 | Installation
2 |
3 | Doorstop requires [Python](https://www.python.org/) and [Git](https://git-scm.com/) or another version control system.
4 |
5 | Once Python is installed on your platform, install Doorstop using pip.
6 |
7 | ```sh
8 | $ pip install doorstop
9 | ```
10 |
11 | !!! note "Installing Pre-releases"
12 | By default, pip only installs stable [releases of Doorstop from PyPi](https://pypi.org/project/doorstop/#history).
13 |
14 | To tell pip to install a pre-release version, [use the `--pre` option](https://pip.pypa.io/en/stable/cli/pip_install/#pre-release-versions):
15 |
16 | ```
17 | $ pip install --pre doorstop
18 | ```
19 |
20 | Alternatively, add it to your [Poetry](https://python-poetry.org/) project:
21 |
22 | ```sh
23 | $ poetry add doorstop
24 | ```
25 |
26 | After installation, Doorstop is available on the command-line:
27 |
28 | ```sh
29 | $ doorstop --help
30 | ```
31 |
32 | And the package is available under the name 'doorstop':
33 |
34 | ```sh
35 | $ python
36 | >>> import doorstop
37 | >>> doorstop.__version__
38 | ```
39 |
--------------------------------------------------------------------------------
/docs/getting-started/quickstart.md:
--------------------------------------------------------------------------------
1 | Quickstart
2 |
3 | Help with Doorstop:
4 |
5 | - `doorstop --help`
6 | - `doorstop --help`
7 |
8 | Set up a repository for controlling documents:
9 |
10 | - `mkdir /path/to/docs`
11 | - `cd /path/to/docs`
12 | - `git init`
13 |
14 | Create a root parent requirements document:
15 |
16 | - `doorstop create REQ ./reqs/req`
17 |
18 | Add items to a document, will be automatically numbered:
19 |
20 | - `doorstop add REQ`
21 |
22 | Create a child document (low level requirements) to link to the parent:
23 |
24 | - `doorstop create LLR ./reqs/llr --parent REQ`
25 | - `doorstop add LLR`
26 |
27 | Link low level items to requirements (separators like '-' are optional and ignored):
28 |
29 | - `doorstop link LLR001 REQ001`
30 |
31 | Check integrity of the document tree and validate links:
32 |
33 | - `doorstop`
34 |
35 | Mark an unreviewed item, document, or all documents as reviewed:
36 |
37 | - `doorstop review REQ-001 # Marks item REQ-001 as reviewed`
38 | - `doorstop review REQ # Marks all items in document REQ as reviewed`
39 | - `doorstop review all # Marks all documents as reviewed`
40 |
41 | Mark suspect links in an item, document, or all documents, as cleared:
42 |
43 | - `doorstop clear LLR-001 # Marks all links originating in item LLR-001 as cleared`
44 | - `doorstop clear LLR-001 REQ # Marks links in item LLR-001 to document REQ as cleared`
45 | - `doorstop clear LLR REQ # Marks all links from LLR that target REQ as cleared`
46 | - `doorstop clear LLR all # Marks all links originating in document LLR as cleared`
47 | - `doorstop clear all # Marks all links in all documents as cleared`
48 |
49 | Create an HTML document in `publish`:
50 |
51 | - `doorstop publish all ./publish`
52 |
53 | View in the graphical user interface (GUI):
54 |
55 | - `doorstop-gui`
56 |
57 | Browse the doc tree on a local web server:
58 |
59 | - `doorstop-server`
60 | - Point browser to: http://127.0.0.1:7867/
61 | - Ctrl+C to quit the server.
62 |
63 | Round-trip to Excel:
64 |
65 | - `doorstop export -x REQ REQ.xslx`
66 | - Make edits in Excel. Do not edit UIDs. Leave UID cells blank for new items.
67 | - `doorstop import REQ.xslx REQ`
68 | - `rm REQ.xlsx`
69 |
--------------------------------------------------------------------------------
/docs/getting-started/setup.md:
--------------------------------------------------------------------------------
1 | ## Editor
2 |
3 | Doorstop will open files using the editor specified by the `$EDITOR` environment variable. If that is unset, it will attempt to open files in the default editor for each file type.
4 |
5 | ## Git
6 |
7 | **Linux / macOS**
8 |
9 | No additional configuration should be necessary.
10 |
11 | **Windows**
12 |
13 | Windows deals with line-endings differently to most systems, favoring `CRLF` (`\r\n`) over the more traditional `LF` (`\n`).
14 | The `YAML` files saved and revision-controlled by Doorstop have `LF`
15 | line-endings, which can cause the following warnings:
16 |
17 | ```
18 | (doorstop) C:\temp\doorstop>doorstop reorder --auto TS
19 | building tree...
20 | reordering document TS...
21 | warning: LF will be replaced by CRLF in tests/sys/TS003.yml.
22 | The file will have its original line endings in your working directory.
23 | warning: LF will be replaced by CRLF in tests/sys/TS001.yml.
24 | The file will have its original line endings in your working directory.
25 | warning: LF will be replaced by CRLF in tests/sys/TS002.yml.
26 | The file will have its original line endings in your working directory.
27 | warning: LF will be replaced by CRLF in tests/sys/TS004.yml.
28 | The file will have its original line endings in your working directory.
29 | reordered document: TS
30 | ```
31 |
32 | These warnings come from Git as a sub-process of the main Doorstop processes,
33 | so the solution is to add the following to your `.gitattributes` file:
34 |
35 | ```
36 | *.yml text eol=lf
37 | ```
38 |
39 | From [Git's documentation](https://git-scm.com/docs/gitattributes):
40 |
41 | > This setting forces Git to normalize line endings [for \*.yml files] to LF on checkin and prevents conversion to CRLF when the file is checked out.
42 |
--------------------------------------------------------------------------------
/docs/gui/desktop-gui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/docs/gui/desktop-gui.png
--------------------------------------------------------------------------------
/docs/gui/overview.md:
--------------------------------------------------------------------------------
1 | # Desktop Client
2 |
3 | An **experimental** desktop graphical interface (GUI) is available as a way to perform basic editing tasks. To launch the program:
4 |
5 | ```sh
6 | $ doorstop-gui
7 | ```
8 |
9 | A window similar to the following should appear:
10 |
11 | 
12 |
13 | If you're interested in helping finish this UI, please check out the [relevant open issues](https://github.com/doorstop-dev/doorstop/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+gui) on GitHub.
14 |
15 | NOTE: You may need to run `brew install python-tk` first on macOS.
16 |
--------------------------------------------------------------------------------
/docs/images/logo-black-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/docs/images/logo-black-white.png
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ../README.md
--------------------------------------------------------------------------------
/docs/reference/document.md:
--------------------------------------------------------------------------------
1 | Document Reference
2 |
3 | Doorstop documents are folders, with metadata stored in a configuration YAML
4 | file `.doorstop.yml` directly below the folder.
5 |
6 | # Standard Document Attributes
7 |
8 | Upon [creation](../cli/creation.md), `.doorstop.yml` contains default settings.
9 |
10 | ```sh
11 | $ doorstop create REQ ./reqs
12 | created document: REQ (@/reqs)
13 | ```
14 |
15 | Then open `REQ/.doorstop.yml` in any text editor:
16 |
17 | ```yaml
18 | settings:
19 | digits: 3
20 | prefix: REQ
21 | itemformat: yaml
22 | sep: ''
23 | ```
24 |
25 | ## `settings`
26 |
27 | Parameters that affect the entire document fall under here.
28 |
29 | ### `digits`
30 |
31 | Defines the number of digits in an item UID. The default value is 3. Optionally,
32 | you can set it through the `-d` command line option of the `doorstop create`
33 | command. It is a mandatory and read-only document setting.
34 |
35 | ### `parent`
36 |
37 | Defines the parent document prefix. You set it through the `-p` command line
38 | option of the `doorstop create` command. It is an optional and read-only
39 | document setting.
40 |
41 | ### `prefix`
42 |
43 | Defines the document prefix. You set it through the prefix of the
44 | `doorstop create` command. It is a mandatory and read-only document setting.
45 |
46 | ### `sep`
47 |
48 | Defines the separator between the document prefix and the number in an item UID.
49 | The default value is the empty string. You have to set it manually before an
50 | item is created. Afterwards, it should be considered as read-only. This
51 | document setting is mandatory.
52 |
53 | ### `itemformat`
54 |
55 | Requirement items can be stored in different file formats. The two types
56 | currently supported are `yaml` and `markdown`. See the [item](item.md)
57 | documentation for more details. The default format is `yaml`.
58 |
59 | # Extended Document Attributes
60 |
61 | In addition to the standard attributes, Doorstop will allow any number of custom
62 | extended attributes (key-value pairs) in the YAML file. The extended attributes
63 | will not be part of a published document, but they can be queried by a 3rd party
64 | application through the REST interface or the Python API.
65 |
66 | ## `attributes`
67 |
68 | Extended document attributes fall under here.
69 |
70 | ### `defaults`
71 |
72 | Defines the [defaults for extended
73 | attributes](item.md#defaults-for-extended-attributes). This is an optional
74 | document configuration option.
75 |
76 | ### `reviewed`
77 |
78 | Defines which [extended attributes contribute to the item
79 | fingerprint](item.md#extended-reviewed-attributes). This is an optional document
80 | configuration option.
81 |
82 | In the document configuration files, you can include other YAML files through a
83 | value tagged with `!include`. The path to the included file is always relative
84 | to the directory of the file with the include tag. Absolute paths are not
85 | supported. Please have a look at this example:
86 |
87 | In `.doorstop.yml`:
88 |
89 | ```yaml
90 | settings:
91 | digits: 3
92 | prefix: REQ
93 | sep: ''
94 | attributes:
95 | defaults:
96 | text: !include path/to/file.yml
97 | ```
98 |
99 | In `path/to/file.yml`:
100 |
101 | ```yaml
102 | |
103 | Some template text, which may
104 | have several
105 | lines.
106 | ```
--------------------------------------------------------------------------------
/docs/reference/tree.md:
--------------------------------------------------------------------------------
1 | Tree Reference
2 |
3 | A Doorstop project structure follows a hierarchal [tree
4 | structure](https://en.wikipedia.org/wiki/Tree_structure).
5 |
6 | The root of the tree can exist anywhere in your version control working copy.
7 | Consider the sample project structure below. (Browning, p. 191)
8 |
9 | ```
10 | req/.doorstop.yml
11 | SRD001.yml
12 | SRD001.YML
13 | tests/.doorstop.yml
14 | HLT001.yml
15 | HLT002.yml
16 | src/doc/.doorstop.yml
17 | SDD001.yml
18 | SSD002.yml
19 | main.c
20 | test/doc/.doorstop.yml
21 | LLT001.yml
22 | LLT002.yml
23 | test_main.c
24 | ```
25 |
26 | In Doorstop each [document](document.md) is a folder with a `.doorstop.yml`
27 | configuration file directly inside it.
28 |
29 | In this sample structure, there are four documents:
30 |
31 | - SRD = Software Requirements Document
32 | - HLT = High Level Test document
33 | - SDD = Software Design Document
34 | - LLT = Low Level Tests document
35 |
36 | A few [item](item.md) files are listed in each document folder that Doorstop has
37 | numbered sequentially.
38 |
39 | # References
40 |
41 | * Browning, Jace, and Robert Adams. 2014. "Doorstop: Text-Based Requirements
42 | Management Using Version Control." Journal of Software Engineering and
43 | Applications 07 (03): 187–94. .
44 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | mkdocs==1.5.3
2 | Pygments==2.17.2
3 |
--------------------------------------------------------------------------------
/docs/web.md:
--------------------------------------------------------------------------------
1 | ## Bottle
2 |
3 | Doorstop can be run as a standalone web server by running
4 | `doorstop-server`.
5 |
6 | It will use the current working directory as the
7 | document source by default.
8 |
9 | ## WSGI
10 |
11 | Doorstop can also be used as a WSGI application by Apache or other web
12 | servers. To configure this, copy `bin/example-adapter.wsgi` from this
13 | repository to an appropriate place in your web data directory, such as
14 | `/var/www/doorstop/adapter.wsgi`. Edit that file to give it the
15 | correct path to your doorstop installation. Now alter your apache
16 | configuration and add something similar to this:
17 |
18 | WSGIDaemonProcess doorstop user=www-data group=www-data processes=1 threads=5
19 |
20 | WSGIScriptAlias /doorstop /var/www/doorstop/adapter.wsgi
21 |
22 | SetEnv DOORSTOP_PROJECT_DIR /path/to/your/document
23 | SetEnv DOORSTOP_BASE_URL /doorstop
24 | WSGIProcessGroup doorstop
25 | WSGIApplicationGroup %{GLOBAL}
26 | Require all granted
27 |
28 |
29 | Change `path/to/your/document` to the path to the Doorstop data you
30 | wish to display.
31 |
--------------------------------------------------------------------------------
/doorstop/__init__.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-only
2 |
3 | """Package for doorstop."""
4 |
5 | from importlib.metadata import PackageNotFoundError, version
6 |
7 | from doorstop.common import DoorstopError, DoorstopInfo, DoorstopWarning
8 | from doorstop.core import (
9 | Document,
10 | Item,
11 | Tree,
12 | build,
13 | builder,
14 | editor,
15 | exporter,
16 | find_document,
17 | find_item,
18 | importer,
19 | publisher,
20 | )
21 |
22 | __project__ = "Doorstop"
23 |
24 | try:
25 | __version__ = version(__project__)
26 | except PackageNotFoundError:
27 | __version__ = "(local)"
28 |
29 | CLI = "doorstop"
30 | GUI = "doorstop-gui"
31 | SERVER = "doorstop-server"
32 | VERSION = "{0} v{1}".format(__project__, __version__)
33 | DESCRIPTION = "Requirements management using version control."
34 |
--------------------------------------------------------------------------------
/doorstop/cli/__init__.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-only
2 |
3 | """Command-line interface for Doorstop."""
4 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-only
2 |
3 | """Package for the doorstop.cli tests."""
4 |
5 | import os
6 | import unittest
7 |
8 | from doorstop import settings
9 | from doorstop.cli.main import main
10 |
11 | ROOT = os.path.join(os.path.dirname(__file__), "..", "..", "..")
12 | REQS = os.path.join(ROOT, "reqs")
13 | TUTORIAL = os.path.join(REQS, "tutorial")
14 | FILES = os.path.join(os.path.dirname(__file__), "files")
15 |
16 | ENV = "TEST_INTEGRATION" # environment variable to enable integration tests
17 | REASON = "'{0}' variable not set".format(ENV)
18 |
19 |
20 | class SettingsTestCase(unittest.TestCase):
21 | """Base test case class that backs up settings."""
22 |
23 | def setUp(self):
24 | self.backup = (
25 | settings.REFORMAT,
26 | settings.CHECK_REF,
27 | settings.CHECK_CHILD_LINKS,
28 | settings.REORDER,
29 | settings.CHECK_LEVELS,
30 | settings.PUBLISH_CHILD_LINKS,
31 | settings.CHECK_SUSPECT_LINKS,
32 | settings.CHECK_REVIEW_STATUS,
33 | settings.PUBLISH_BODY_LEVELS,
34 | settings.CACHE_DOCUMENTS,
35 | settings.CACHE_ITEMS,
36 | settings.CACHE_PATHS,
37 | settings.WARN_ALL,
38 | settings.ERROR_ALL,
39 | settings.SERVER_HOST,
40 | settings.SERVER_PORT,
41 | )
42 |
43 | def tearDown(self):
44 | (
45 | settings.REFORMAT,
46 | settings.CHECK_REF,
47 | settings.CHECK_CHILD_LINKS,
48 | settings.REORDER,
49 | settings.CHECK_LEVELS,
50 | settings.PUBLISH_CHILD_LINKS,
51 | settings.CHECK_SUSPECT_LINKS,
52 | settings.CHECK_REVIEW_STATUS,
53 | settings.PUBLISH_BODY_LEVELS,
54 | settings.CACHE_DOCUMENTS,
55 | settings.CACHE_ITEMS,
56 | settings.CACHE_PATHS,
57 | settings.WARN_ALL,
58 | settings.ERROR_ALL,
59 | settings.SERVER_HOST,
60 | settings.SERVER_PORT,
61 | ) = self.backup
62 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/docs/.doorstop.yml:
--------------------------------------------------------------------------------
1 | settings:
2 | digits: 3
3 | parent: TUT
4 | prefix: HLT
5 | sep: ''
6 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/docs/HLT001.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.1
5 | links:
6 | - TUT001: UdIKq3W6wFQh-0c_VH39gf_h_ylXOAhobQ-ULSgiH-Y=
7 | - TUT002: yJH1DjAtUch02wejY1yhbglJskFa9mf2VgusvEF03vU=
8 | - TUT004: I36aGjE75oB5vbPvUlM_gXuyLh_56CfVmYuAcEzsa6U=
9 | - TUT008: z3Xivwhj4Ii69-OvVs7lj1qt4owdivQLvMi7wMGxL6Y=
10 | - TUT017: JvHYGJBTVYk--HFVudf5PMOvTHZuikZf5trmX1TKikE=
11 | - TUT019: Wg_m37OqgVhFw9YvjZkxv9mFdrg04ZIy8u1NLg4Pvh0=
12 | normative: true
13 | ref: test_tutorial_section_1
14 | reviewed: LuZMejVVbNoVI-fZff7mrEqiORKLfSvW0IYGX1CTafA=
15 | text: |
16 | Tutorial Section 1.0:
17 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/docs/HLT002.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.2
5 | links:
6 | - TUT009: A_rTJjsI_7B9QRUOWaqSBxquBdz1t9h_HwNn3cQXtQQ=
7 | - TUT010: zgtZEugq0wM6J06MTFhb_kGQtUdus1Qdi_HTc-ZiReI=
8 | normative: true
9 | ref: test_tutorial_section_2
10 | reviewed: zXSVvXJUdWg5mMc78SyoUg7iFagz82_GKz1uDSK0tAA=
11 | text: |
12 | Tutorial Section 2.0:
13 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/docs/HLT003.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.3
5 | links:
6 | - TUT012: VkQ9YnIdcmX4-9b9WpMwi2Ghqd4Tg5qMn8pqrGGmXas=
7 | - TUT013: 7lqiX5VtcvclAHuCzl7mqVr17Xu1PEQNuquw80DNDSQ=
8 | - TUT016: ZA0D9hs_RcGQvBR3EjlVdAYRp1lonVCBBq29AVU-vPE=
9 | normative: true
10 | ref: test_tutorial_section_3
11 | reviewed: aME0_SIctRosiaSzFJLZSugR1lZfDkgnSXyPiMUQd-A=
12 | text: |
13 | Tutorial Section 3.0:
14 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/docs/HLT004.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.4
5 | links:
6 | - TUT015: ClzVApdcWd-VQIuz7XN_SRyRvlFUVD9A6XONsF9ZNT4=
7 | normative: true
8 | ref: test_tutorial_section_4
9 | reviewed: uqurqigZ0qa92QvdApGpiaV4htLBKHLOZT0xoOOe6-8=
10 | text: |
11 | Tutorial Section 4.0:
12 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/docs/HLT005.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.0
5 | links: []
6 | normative: false
7 | ref: ''
8 | reviewed: b15hY4KHGmxUEWzRPFSCWv5ToVl0lbsdIgTv8O5xVA0=
9 | text: |
10 | Automated Tests
11 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/A001.txt:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.0
5 | links:
6 | - B001: xaO5x-ZxaHLy2knS_DD73aXtVRV3hxqCmJInnCh2PZY=
7 | normative: true
8 | ref: ''
9 | reviewed: JEp17AY60onnnNJH7zLSYmM5O2d3heNVB96iEGFkX48=
10 | text: |
11 | A001
12 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/A002.txt:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.1
5 | links:
6 | - A002: xwOeM6ISel7jEK2qfGlKduG-u5zjhv0uybQrC61UbDQ=
7 | normative: true
8 | ref: ''
9 | reviewed: 4jsCT-Eq-WsIKbMoEV-tzo4AJ_I2_Bs0laFUehmJETg=
10 | text: |
11 | A002
12 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/B001.txt:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.0
5 | links:
6 | - B002: gDTkEwZSPXcHS-l4h25uOvA8kPtZ6kF0srVr6KbD2Zo=
7 | normative: true
8 | ref: ''
9 | reviewed: GZSV6eLVc5kAfY6bwMSAT1oTHouyBLJHLOUDJu5E-J8=
10 | text: |
11 | B001
12 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/B002.txt:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.1
5 | links:
6 | - A001: tVIIYUpx8ypqwRceNq8fPnXtMRBkuVJEcJHF-0oVya8=
7 | - A002: xwOeM6ISel7jEK2qfGlKduG-u5zjhv0uybQrC61UbDQ=
8 | normative: true
9 | ref: ''
10 | reviewed: 64iJUOaTCc2Ufto1KD7_B5Rj2mthKNbuElJvZI52n9k=
11 | text: |
12 | B002
13 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/C001.txt:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.0
5 | links:
6 | - C002: ThisIsTheFingerprintOfASuspectLinkABCDEFGHI=
7 | - C003: ThisIsTheFingerprintOfASuspectLinkABCDEFGHI=
8 | normative: true
9 | ref: ''
10 | reviewed: czv54TPcUklkenTWIS96RvaEKb_vYY9wKgwD5TJcKsA=
11 | text: |
12 | C001
13 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/C002.txt:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.1
5 | links: []
6 | normative: true
7 | ref: ''
8 | reviewed: d05UOdrvVooFi8X_3XHRFPrHG3d5DE5yH0sc1dIaZ64=
9 | text: |
10 | C002
11 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/C003.txt:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.2
5 | links: []
6 | normative: true
7 | ref: ''
8 | reviewed: QERl1YZnr5RYggS6Pkf5Sibzh9FJ0CfDGGFe6lDpY5U=
9 | text: |
10 | C003
11 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/exported-map.csv:
--------------------------------------------------------------------------------
1 | uid,mylevel,text,ref,links,active,derived,normative
2 | REQ001,1.2.3,"Hello, world!
3 | ",,"SYS001,
4 | SYS002",True,False,True
5 | REQ003,1.4,"Hello, world!
6 | ",REF123,REQ001,True,False,True
7 | REQ004,1.6,"Hello, world!
8 | ",,,True,False,True
9 | REQ002,2.1,"Hello, world!
10 | ",,,True,False,True
11 | invalid,2.1,"Hello, world!
12 | ",,REQ001,True,False,True
13 | REQ2-001,2.1,"Hello, world!
14 | ",,REQ001,True,False,True
15 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/exported.csv:
--------------------------------------------------------------------------------
1 | uid,level,text,ref,links,active,derived,normative
2 | REQ001,1.2.3,"Hello, world!
3 | ",,"SYS001,
4 | SYS002",True,False,True
5 | REQ003,1.4,"Hello, world!
6 | ",REF123,REQ001,True,False,True
7 | REQ004,1.6,"Hello, world!
8 | ",,,True,False,True
9 | REQ002,2.1,"Hello, world!
10 | ",,,True,False,True
11 | invalid,2.1,"Hello, world!
12 | ",,REQ001,True,False,True
13 | REQ2-001,2.1,"Hello, world!
14 | ",,REQ001,True,False,True
15 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/exported.tsv:
--------------------------------------------------------------------------------
1 | uid level text ref links active derived normative
2 | REQ001 1.2.3 "Hello, world!
3 | " "SYS001,
4 | SYS002" True False True
5 | REQ003 1.4 "Hello, world!
6 | " REF123 REQ001 True False True
7 | REQ004 1.6 "Hello, world!
8 | " True False True
9 | REQ002 2.1 "Hello, world!
10 | " True False True
11 | REQ2-001 2.1 "Hello, world!
12 | " REQ001 True False True
13 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/exported.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/cli/tests/files/exported.xlsx
--------------------------------------------------------------------------------
/doorstop/cli/tests/files/template.yml:
--------------------------------------------------------------------------------
1 | text: |
2 | Some text
3 | with more than
4 | one line.
5 |
--------------------------------------------------------------------------------
/doorstop/cli/tests/test_main.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-only
2 |
3 | """Unit tests for the doorstop.cli.main module."""
4 |
5 | import importlib.util
6 | import sys
7 | from os import sep
8 | from unittest.mock import Mock, patch
9 |
10 | from doorstop import settings
11 | from doorstop.cli import main
12 | from doorstop.cli.tests import SettingsTestCase
13 |
14 |
15 | class TestMain(SettingsTestCase):
16 | """Unit tests for the `main` function."""
17 |
18 | @patch("doorstop.cli.commands.get")
19 | def test_run(self, mock_get):
20 | """Verify the main CLI function can be called."""
21 | main.main(args=[])
22 | mock_get.assert_called_once_with(None)
23 |
24 | @patch("doorstop.cli.commands.run", Mock(side_effect=KeyboardInterrupt))
25 | def test_interrupt(self):
26 | """Verify the CLI can be interrupted."""
27 | self.assertRaises(SystemExit, main.main, [])
28 |
29 | @patch("doorstop.cli.commands.run", Mock())
30 | def test_empty(self):
31 | """Verify 'doorstop' can be run in a working copy with no docs."""
32 | self.assertIs(None, main.main([]))
33 | self.assertTrue(settings.REFORMAT)
34 | self.assertTrue(settings.CHECK_REF)
35 | self.assertTrue(settings.CHECK_CHILD_LINKS)
36 | self.assertFalse(settings.REORDER)
37 | self.assertTrue(settings.CHECK_LEVELS)
38 | self.assertTrue(settings.CHECK_SUSPECT_LINKS)
39 | self.assertTrue(settings.CHECK_REVIEW_STATUS)
40 | self.assertTrue(settings.CACHE_DOCUMENTS)
41 | self.assertTrue(settings.CACHE_ITEMS)
42 | self.assertTrue(settings.CACHE_PATHS)
43 | self.assertFalse(settings.WARN_ALL)
44 | self.assertFalse(settings.ERROR_ALL)
45 |
46 | @patch("doorstop.cli.commands.run", Mock())
47 | def test_options(self):
48 | """Verify 'doorstop' can be run with options."""
49 | self.assertIs(
50 | None,
51 | main.main(
52 | [
53 | "--no-reformat",
54 | "--no-ref-check",
55 | "--no-child-check",
56 | "--reorder",
57 | "--no-level-check",
58 | "--no-suspect-check",
59 | "--no-review-check",
60 | "--no-cache",
61 | "--warn-all",
62 | "--error-all",
63 | ]
64 | ),
65 | )
66 | self.assertFalse(settings.REFORMAT)
67 | self.assertFalse(settings.CHECK_REF)
68 | self.assertFalse(settings.CHECK_CHILD_LINKS)
69 | self.assertTrue(settings.REORDER)
70 | self.assertFalse(settings.CHECK_LEVELS)
71 | self.assertFalse(settings.CHECK_SUSPECT_LINKS)
72 | self.assertFalse(settings.CHECK_REVIEW_STATUS)
73 | self.assertFalse(settings.CACHE_DOCUMENTS)
74 | self.assertFalse(settings.CACHE_ITEMS)
75 | self.assertFalse(settings.CACHE_PATHS)
76 | self.assertTrue(settings.WARN_ALL)
77 | self.assertTrue(settings.ERROR_ALL)
78 |
79 | def test_main(self):
80 | testargs = [sep.join(["doorstop", "cli", "main.py"])]
81 | with patch.object(sys, "argv", testargs):
82 | spec = importlib.util.spec_from_file_location("__main__", testargs[0])
83 | runpy = importlib.util.module_from_spec(spec)
84 | spec.loader.exec_module(runpy)
85 | # Assert
86 | self.assertIsNotNone(runpy)
87 |
--------------------------------------------------------------------------------
/doorstop/core/__init__.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-only
2 |
3 | """Core package for Doorstop."""
4 |
5 | from doorstop.core.builder import build, find_document, find_item
6 | from doorstop.core.document import Document
7 | from doorstop.core.item import Item
8 | from doorstop.core.tree import Tree
9 |
--------------------------------------------------------------------------------
/doorstop/core/editor.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-only
2 |
3 | """Functions to edit documents and items."""
4 |
5 | import os
6 | import shutil
7 | import subprocess
8 | import sys
9 | import tempfile
10 | import time
11 |
12 | from doorstop import common
13 | from doorstop.common import DoorstopError
14 |
15 | LAUNCH_DELAY = 0.5 # number of seconds to let a program try to launch
16 |
17 | log = common.logger(__name__)
18 |
19 |
20 | def edit(path, tool=None):
21 | """Open a file and wait for the default editor to exit.
22 |
23 | :param path: path of file to open
24 | :param tool: path of alternate editor
25 |
26 | :return: launched process
27 |
28 | """
29 | process = launch(path, tool=tool)
30 | if process:
31 | try:
32 | process.wait()
33 | except KeyboardInterrupt:
34 | log.debug("user cancelled")
35 | finally:
36 | if process.returncode is None:
37 | process.terminate()
38 | log.warning("force closed editor")
39 | log.debug("process exited: {}".format(process.returncode))
40 |
41 |
42 | def edit_tmp_content(title=None, original_content=None, tool=None):
43 | """Edit content in a temporary file and return the saved content.
44 |
45 | :param title: text that will appear in the name of the temporary file.
46 | If not given, name is only random characters.
47 | :param original_content: content to insert in the temporary file before
48 | opening it with the editor. If not given, file is empty.
49 | Must be a string object.
50 | :param tool: path of alternate editor
51 |
52 | :return: content of the temporary file after user closes the editor.
53 |
54 | """
55 | # Create a temporary file to edit the text
56 | tmp_fd, tmp_path = tempfile.mkstemp(prefix="{}_".format(title), text=True)
57 | os.close(tmp_fd) # release the file descriptor because it is not needed
58 | with open(tmp_path, "w") as tmp_f:
59 | tmp_f.write(original_content)
60 |
61 | # Open the editor to edit the temporary file with the original text
62 | edit(tmp_path, tool=tool)
63 |
64 | # Read the edited text and remove the tmp file
65 | with open(tmp_path, "r") as tmp_f:
66 | edited_content = tmp_f.read()
67 | os.remove(tmp_path)
68 |
69 | return edited_content
70 |
71 |
72 | def launch(path, tool=None):
73 | """Open a file using the default editor.
74 |
75 | :param path: path of file to open
76 | :param tool: path of alternate editor
77 |
78 | :raises: :class:`~doorstop.common.DoorstopError` no default editor
79 | or editor unavailable
80 |
81 | :return: launched process if long-running, else None
82 |
83 | """
84 | # Determine how to launch the editor
85 | if tool:
86 | args = [tool, path]
87 | elif sys.platform.startswith("darwin"):
88 | args = ["open", path]
89 | elif os.name == "nt":
90 | cygstart = shutil.which("cygstart")
91 | if cygstart:
92 | args = [cygstart, path]
93 | else:
94 | args = ["start", path]
95 | elif os.name == "posix":
96 | args = ["xdg-open", path]
97 | else:
98 | raise DoorstopError(
99 | "unknown operating system, unable to determine launch command"
100 | )
101 |
102 | # Launch the editor
103 | try:
104 | log.info("opening '{}'...".format(path))
105 | process = _call(args)
106 | except FileNotFoundError:
107 | raise DoorstopError("editor not found: {}".format(args[0]))
108 |
109 | # Wait for the editor to launch
110 | time.sleep(LAUNCH_DELAY)
111 | if process.poll() is None:
112 | log.debug("process is running...")
113 | else:
114 | log.debug("process exited: {}".format(process.returncode))
115 | if process.returncode != 0:
116 | raise DoorstopError("no default editor for: {}".format(path))
117 |
118 | # Return the process if it's still running
119 | return process if process.returncode is None else None
120 |
121 |
122 | def _call(args):
123 | """Call a program with arguments and return the process."""
124 | log.debug("$ {}".format(" ".join(args)))
125 | process = subprocess.Popen(args)
126 | return process
127 |
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/doorstop.css:
--------------------------------------------------------------------------------
1 | /* Doorstop.css file. */
2 | .caption {
3 | text-align: center;
4 | font-size:15px;
5 | }
6 |
7 | #img {width: 100%}
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/general.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-left: 0px;
3 | padding-top: 0px;
4 | padding-right: 0px;
5 | }
6 |
7 | /* indent everything within a section */
8 | section > * {
9 | margin-left: 30px;
10 | }
11 |
12 | /* don't indent the section header */
13 | section > :first-child {
14 | margin-left: 0px;
15 | }
16 |
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/logo-black-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/logo-black-white.png
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_AMS-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_AMS-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Calligraphic-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Calligraphic-Bold.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Calligraphic-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Calligraphic-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Fraktur-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Fraktur-Bold.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Fraktur-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Fraktur-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Main-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Main-Bold.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Main-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Main-Italic.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Main-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Main-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Math-BoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Math-BoldItalic.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Math-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Math-Italic.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Math-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Math-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_SansSerif-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_SansSerif-Bold.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_SansSerif-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_SansSerif-Italic.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_SansSerif-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_SansSerif-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Script-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Script-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Size1-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Size1-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Size2-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Size2-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Size3-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Size3-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Size4-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Size4-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Typewriter-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Typewriter-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Vector-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Vector-Bold.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Vector-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Vector-Regular.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Zero.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/html/output/chtml/fonts/woff-v2/MathJax_Zero.woff
--------------------------------------------------------------------------------
/doorstop/core/files/templates/latex/doorstop.yml:
--------------------------------------------------------------------------------
1 | # Define all options to class.
2 | documentclass:
3 | - a4paper
4 | - twoside
5 | # Define all 'usepackage' commands.
6 | usepackage:
7 | inputenc:
8 | - utf8
9 | amsmath:
10 | ulem:
11 | longtable:
12 | fancyvrb:
13 | xr-hyper:
14 | hyperref:
15 | # Options are defined as sub-lists.
16 | - unicode
17 | - colorlinks
18 | zref-user:
19 | zref-xr:
20 | # Define custom lines before \begin{document}
21 | before_begin_document:
22 | - \def\docname{Doorstop - \doccategory{}}
23 | # Define custom lines after \begin{document}
24 | after_begin_document:
25 | - \maketitle
26 | - \maketoc
27 |
--------------------------------------------------------------------------------
/doorstop/core/files/templates/latex/logo-black-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/files/templates/latex/logo-black-white.png
--------------------------------------------------------------------------------
/doorstop/core/publishers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/publishers/__init__.py
--------------------------------------------------------------------------------
/doorstop/core/publishers/tests/helpers.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-only
2 |
3 | """Unit test helper functions to reduce code duplication."""
4 |
5 | # pylint: disable=unused-argument,protected-access
6 |
7 | import os
8 |
9 | LINES = """
10 | initial: 1.2.3
11 | outline:
12 | - REQ001: # Lorem ipsum d...
13 | - REQ003: # Unicode: -40° ±1%
14 | - REQ004: # Hello, world! !['..
15 | - REQ002: # Hello, world! !["...
16 | - REQ2-001: # Hello, world!
17 | """
18 | YAML_CUSTOM_ATTRIBUTES = """
19 | settings:
20 | digits: 3
21 | prefix: REQ
22 | sep: '-'
23 | attributes:
24 | defaults:
25 | publish:
26 | - CUSTOM-ATTRIB
27 | - invented-by
28 | """
29 | HTML_TEMPLATE_WALK = """
30 | template/
31 | bootstrap.bundle.min.js
32 | bootstrap.min.css
33 | doorstop.css
34 | general.css
35 | jquery.min.js
36 | logo-black-white.png
37 | tex-mml-chtml.js
38 | output/
39 | chtml.js
40 | svg.js
41 | chtml/
42 | fonts/
43 | tex.js
44 | woff-v2/
45 | MathJax_AMS-Regular.woff
46 | MathJax_Calligraphic-Bold.woff
47 | MathJax_Calligraphic-Regular.woff
48 | MathJax_Fraktur-Bold.woff
49 | MathJax_Fraktur-Regular.woff
50 | MathJax_Main-Bold.woff
51 | MathJax_Main-Italic.woff
52 | MathJax_Main-Regular.woff
53 | MathJax_Math-BoldItalic.woff
54 | MathJax_Math-Italic.woff
55 | MathJax_Math-Regular.woff
56 | MathJax_SansSerif-Bold.woff
57 | MathJax_SansSerif-Italic.woff
58 | MathJax_SansSerif-Regular.woff
59 | MathJax_Script-Regular.woff
60 | MathJax_Size1-Regular.woff
61 | MathJax_Size2-Regular.woff
62 | MathJax_Size3-Regular.woff
63 | MathJax_Size4-Regular.woff
64 | MathJax_Typewriter-Regular.woff
65 | MathJax_Vector-Bold.woff
66 | MathJax_Vector-Regular.woff
67 | MathJax_Zero.woff
68 | svg/
69 | fonts/
70 | tex.js
71 | views/
72 | base.tpl
73 | document_list.tpl
74 | doorstop.tpl
75 | item_list.tpl
76 | """
77 |
78 |
79 | def getWalk(walk_path):
80 | # Get the exported tree.
81 | walk = []
82 | for root, _, files in sorted(os.walk(walk_path)):
83 | level = root.replace(walk_path, "").count(os.sep)
84 | indent = " " * 4 * (level)
85 | walk.append("{}{}/\n".format(indent, os.path.basename(root)))
86 | subindent = " " * 4 * (level + 1)
87 | for f in sorted(files):
88 | walk.append("{}{}\n".format(subindent, f))
89 | return "".join(line + "" for line in walk)
90 |
91 |
92 | def getLines(gen):
93 | # Get the generated lines.
94 | result = ""
95 | for line in gen:
96 | result = result + line + "\n"
97 | return result
98 |
99 |
100 | def getFileContents(file):
101 | """Return the contents of a file."""
102 | data = []
103 | with open(file, "r") as file_stream:
104 | data = file_stream.readlines()
105 | return data
106 |
--------------------------------------------------------------------------------
/doorstop/core/publishers/tests/helpers_latex.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-only
2 |
3 | """Unit test helper functions to reduce code duplication."""
4 |
5 | # pylint: disable=unused-argument,protected-access
6 |
7 | YAML_LATEX_DOC = """
8 | settings:
9 | digits: 3
10 | prefix: REQ
11 | sep: '-'
12 | attributes:
13 | defaults:
14 | doc:
15 | name: 'Tutorial'
16 | title: 'Development test document'
17 | ref: 'TUT-DS-22'
18 | by: 'Jng'
19 | major: '1'
20 | minor: 'A'
21 | copyright: 'Whatever Inc.'
22 | publish:
23 | - CUSTOM-ATTRIB
24 | - invented-by
25 | """
26 |
27 | YAML_LATEX_EMPTY_DOC = """
28 | settings:
29 | digits: 3
30 | prefix: TST
31 | sep: '-'
32 | attributes:
33 | defaults:
34 | doc:
35 | name: ''
36 | title: ''
37 | ref: ''
38 | by: ''
39 | major: ''
40 | minor: ''
41 | copyright: ''
42 | publish:
43 | - CUSTOM-ATTRIB
44 | """
45 |
46 | YAML_LATEX_NO_DOC = """
47 | settings:
48 | digits: 3
49 | prefix: TST
50 | sep: '-'
51 | attributes:
52 | defaults:
53 | publish:
54 | - CUSTOM-ATTRIB
55 | """
56 |
57 | YAML_LATEX_NO_REF = """
58 | settings:
59 | digits: 3
60 | prefix: TST
61 | sep: '-'
62 | attributes:
63 | defaults:
64 | doc:
65 | name: 'Tutorial'
66 | title: 'Development test document'
67 | by: 'Jng'
68 | major: '1'
69 | minor: 'A'
70 | copyright: 'Whatever Inc.'
71 | publish:
72 | - CUSTOM-ATTRIB
73 | - invented-by
74 | """
75 |
76 | YAML_LATEX_ONLY_REF = """
77 | settings:
78 | digits: 3
79 | prefix: TST
80 | sep: '-'
81 | attributes:
82 | defaults:
83 | doc:
84 | ref: 'TUT-DS-22'
85 | publish:
86 | - CUSTOM-ATTRIB
87 | - invented-by
88 | """
89 |
--------------------------------------------------------------------------------
/doorstop/core/publishers/tests/test_publisher_html_doc.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-only
2 |
3 | """Unit tests for the doorstop.core.publishers.html module."""
4 |
5 | # pylint: disable=unused-argument,protected-access
6 |
7 | import os
8 | import stat
9 | import unittest
10 | from secrets import token_hex
11 | from shutil import rmtree
12 |
13 | from doorstop.common import DoorstopError
14 | from doorstop.core import publisher
15 | from doorstop.core.builder import build
16 | from doorstop.core.publishers.tests.helpers import HTML_TEMPLATE_WALK, getWalk
17 | from doorstop.core.tests import ROOT, MockDataMixIn
18 | from doorstop.core.tests.helpers import on_error_with_retry
19 |
20 |
21 | class TestPublisherFullDocument(MockDataMixIn, unittest.TestCase):
22 | """Unit tests for the doorstop.core.publishers.html module by publishing a full document tree."""
23 |
24 | # pylint: disable=no-value-for-parameter
25 | def setUp(self):
26 | """Setup test folder."""
27 | # Build a tree.
28 | self.mock_tree = build(cwd=ROOT, root=ROOT, request_next_number=None)
29 | self.hex = token_hex()
30 | self.dirpath = os.path.abspath(os.path.join("mock_%s" % __name__, self.hex))
31 | os.makedirs(self.dirpath)
32 | self.expected_walk = """{n}/
33 | index.html
34 | traceability.csv
35 | traceability.html
36 | documents/
37 | EXT.html
38 | HLT.html
39 | LLT.html
40 | REQ.html
41 | TUT.html
42 | assets/
43 | logo-black-white.png{w}""".format(
44 | n=self.hex, w=HTML_TEMPLATE_WALK
45 | )
46 |
47 | @classmethod
48 | def tearDownClass(cls):
49 | """Remove test folder."""
50 | rmtree("mock_%s" % __name__, onerror=on_error_with_retry)
51 |
52 | def test_publish_html_tree_copies_assets(self):
53 | """Verify that html assets are published when publishing a tree."""
54 | # Act
55 | path2 = publisher.publish(self.mock_tree, self.dirpath, ext=".html")
56 | # Assert
57 | self.assertIs(self.dirpath, path2)
58 | # Get the exported tree.
59 | walk = getWalk(self.dirpath)
60 | self.assertEqual(self.expected_walk, walk)
61 |
62 | def test_bad_html_template(self):
63 | """Verify a bad HTML template raises an error."""
64 | # Act
65 | with self.assertRaises(DoorstopError):
66 | publisher.publish(self.mock_tree, ".html", template="DOES_NOT_EXIST")
67 |
--------------------------------------------------------------------------------
/doorstop/core/publishers/tests/test_publisher_markdown_doc.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-only
2 |
3 | """Unit tests for the doorstop.core.publishers.markdown module."""
4 |
5 | # pylint: disable=unused-argument,protected-access
6 |
7 | import os
8 | import stat
9 | import unittest
10 | from secrets import token_hex
11 | from shutil import rmtree
12 |
13 | from doorstop.core import publisher
14 | from doorstop.core.builder import build
15 | from doorstop.core.publishers.tests.helpers import getWalk
16 | from doorstop.core.tests import ROOT, MockDataMixIn, MockDocument
17 | from doorstop.core.tests.helpers import on_error_with_retry
18 |
19 |
20 | class TestPublisherFullDocument(MockDataMixIn, unittest.TestCase):
21 | """Unit tests for the doorstop.core.publishers.markdown module by publishing a full document tree."""
22 |
23 | # pylint: disable=no-value-for-parameter
24 | def setUp(self):
25 | """Setup test folder."""
26 | # Build a tree.
27 | self.mock_tree = build(cwd=ROOT, root=ROOT, request_next_number=None)
28 | self.hex = token_hex()
29 | self.dirpath = os.path.abspath(os.path.join("mock_%s" % __name__, self.hex))
30 | os.makedirs(self.dirpath)
31 | self.expected_walk = """{n}/
32 | EXT.md
33 | HLT.md
34 | LLT.md
35 | REQ.md
36 | TUT.md
37 | assets/
38 | logo-black-white.png
39 | """.format(
40 | n=self.hex
41 | )
42 |
43 | @classmethod
44 | def tearDownClass(cls):
45 | """Remove test folder."""
46 | rmtree("mock_%s" % __name__, onerror=on_error_with_retry)
47 |
48 | def test_publish_markdown_tree_copies_assets(self):
49 | """Verify that markdown assets are published when publishing a tree."""
50 | # Act
51 | path2 = publisher.publish(self.mock_tree, self.dirpath, ext=".md")
52 | # Assert
53 | self.assertIs(self.dirpath, path2)
54 | # Get the exported tree.
55 | walk = getWalk(self.dirpath)
56 | self.assertEqual(self.expected_walk, walk)
57 |
58 | def test_publish_markdown_document_to_path(self):
59 | """Verify that single document export to path works."""
60 | expected_walk = """{n}/
61 | REQ.md
62 | """.format(
63 | n=self.hex
64 | )
65 | dirpath = self.dirpath
66 | doc = MockDocument(dirpath)
67 | # Act
68 | path2 = publisher.publish(doc, dirpath, ".md")
69 | # Assert
70 | self.assertIs(dirpath, path2)
71 | # Get the exported tree.
72 | walk = getWalk(self.dirpath)
73 | self.assertEqual(expected_walk, walk)
74 |
--------------------------------------------------------------------------------
/doorstop/core/reference_finder.py:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-only
2 |
3 | """Finding external references."""
4 |
5 | import linecache
6 | import os
7 | import re
8 |
9 | from doorstop import common, settings
10 | from doorstop.common import DoorstopError
11 |
12 | log = common.logger(__name__)
13 |
14 |
15 | class ReferenceFinder:
16 | """Finds files referenced from an Item."""
17 |
18 | @staticmethod
19 | def find_ref(ref, tree, item_path):
20 | """Get the external file reference and line number.
21 |
22 | :raises: :class:`~doorstop.common.DoorstopError` when no
23 | reference is found
24 |
25 | :return: relative path to file or None (when no reference
26 | set),
27 | line number (when found in file) or None (when found as
28 | filename) or None (when no reference set)
29 |
30 | """
31 |
32 | # Search for the external reference
33 | log.debug("searching for ref '{}'...".format(ref))
34 | pattern = r"(\b|\W){}(\b|\W)".format(re.escape(ref))
35 | log.trace("regex: {}".format(pattern)) # type: ignore
36 | regex = re.compile(pattern)
37 | for path, filename, relpath in tree.vcs.paths:
38 | # Skip the item's file while searching
39 | if path == item_path:
40 | continue
41 | # Check for a matching filename
42 | if filename == ref:
43 | return relpath, None
44 | # Skip extensions that should not be considered text
45 | if os.path.splitext(filename)[-1] in settings.SKIP_EXTS:
46 | continue
47 | # Search for the reference in the file
48 | try:
49 | lines = linecache.getlines(path)
50 | except (SyntaxError, UnicodeDecodeError):
51 | log.trace("unable to read lines from: {}".format(path)) # type: ignore
52 | continue
53 | for lineno, line in enumerate(lines, start=1):
54 | if regex.search(line):
55 | log.debug("found ref: {}".format(relpath))
56 | return relpath, lineno
57 |
58 | msg = "external reference not found: {}".format(ref)
59 | raise DoorstopError(msg)
60 |
61 | @staticmethod
62 | def find_file_reference(ref_path, root, tree, item_path, keyword=None):
63 | """Find the external file reference.
64 |
65 | :raises: :class:`~doorstop.common.DoorstopError` when no
66 | reference is found
67 |
68 | :return: Tuple (ref_path, line) when reference is found
69 |
70 | """
71 |
72 | log.debug("searching for ref '{}'...".format(ref_path))
73 | ref_full_path = os.path.normpath(os.path.join(root, ref_path))
74 |
75 | for path, _filename, relpath in tree.vcs.paths:
76 | # Skip the item's file while searching
77 | if path == item_path:
78 | continue
79 | if path == ref_full_path:
80 | if keyword is None:
81 | return relpath, None
82 |
83 | # Search for the reference in the file
84 | try:
85 | lines = linecache.getlines(path)
86 | except SyntaxError:
87 | log.trace("unable to read lines from: {}".format(path)) # type: ignore
88 | continue
89 |
90 | log.debug("searching for ref '{}'...".format(keyword))
91 | pattern = r"(\b|\W){}(\b|\W)".format(re.escape(keyword))
92 | log.trace("regex: {}".format(pattern)) # type: ignore
93 | regex = re.compile(pattern)
94 | for lineno, line in enumerate(lines, start=1):
95 | if regex.search(line):
96 | log.debug("found ref: {}".format(relpath))
97 | return relpath, lineno
98 |
99 | msg = "external reference not found: {}".format(ref_path)
100 | raise DoorstopError(msg)
101 |
--------------------------------------------------------------------------------
/doorstop/core/tests/docs/.doorstop.yml:
--------------------------------------------------------------------------------
1 | settings:
2 | digits: 3
3 | parent: REQ
4 | prefix: LLT
5 | sep: ''
6 |
--------------------------------------------------------------------------------
/doorstop/core/tests/docs/LLT001.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.1
5 | links:
6 | - REQ003: 9TcFUzsQWUHhoh5wsqnhL7VRtSqMaIhrCXg7mfIkxKM=
7 | normative: true
8 | ref: Verify an item can be added to a document.
9 | reviewed: 3cWsswJTpxB9WHng7lXLeM4jQW6zbPTZtt1vL1mZFMI=
10 | text: |
11 | Test adding items:
12 |
--------------------------------------------------------------------------------
/doorstop/core/tests/docs/LLT002.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.2
5 | links:
6 | - REQ004: T2tSkn27DO3GXvagwOgNNLvhW4FPNg9gyLfru-l9hWQ=
7 | normative: true
8 | ref: Verify Markdown can be published from a document.
9 | reviewed: ly_FQiijvn6dMCKTGW5gzw3zovTXRmB5g_WFv39j0nA=
10 | text: |
11 | Test publishing Markdown:
12 |
--------------------------------------------------------------------------------
/doorstop/core/tests/docs/LLT003.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.3
5 | links:
6 | - REQ007: N4qTPlDi0z6kClsYAWlTsYPYWPylyr5KscMlxyYlzbA=
7 | normative: true
8 | ref: Verify text can be published from a document.
9 | reviewed: nZXA_TD_MfctNFPxpkuwjeucmfGh3588iDeLsaExROA=
10 | text: |
11 | Test publishing text:
12 |
--------------------------------------------------------------------------------
/doorstop/core/tests/docs/LLT004.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.4
5 | links:
6 | - REQ008: Y9QwGNJVzJSHbW9sHzBqswqAtF5v8OJup8HEH7E2qHU=
7 | normative: true
8 | ref: Verify the items in a document can be accessed.
9 | reviewed: vD17pIGCoBclRlwG71wnHHt_Ng1jkCo4uq2h6hOYBWw=
10 | text: |
11 | Test getting items from a document:
12 |
--------------------------------------------------------------------------------
/doorstop/core/tests/docs/LLT005.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.5
5 | links:
6 | - REQ001: avwblqPimDJ2OgTrRCXxRPN8FQhUBWqPIXm7kSR95C4=
7 | normative: true
8 | ref: Verify an item's reference can also be a filename.
9 | reviewed: G96xqQH4CjV1i3ZwiWtUKbbDisOK28E7ik8aItcy07A=
10 | text: |
11 | Test referencing an external file by name:
12 |
--------------------------------------------------------------------------------
/doorstop/core/tests/docs/LLT007.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 2.1
5 | links:
6 | - REQ009: uuTiyRLtSfUnneuE71WqML8D_X8YtiKj02Ehlb8b2bg=
7 | - REQ011: 3N-eJ3o7Va-Vwx8F4VCE1eYw4WCwK1DleYM95RfsaEQ=
8 | - REQ012: aDEVUTCV4yqDY99sfjch7otkgidiyG0We8tGSTWD9Hs=
9 | - REQ013: ch1ilz7OJnQhSuKbpcN81Z0ml2z6lBTQqV6rWanW_ZA=
10 | - REQ014: r_QHE6crBVcD_cEeXXtztbaeU7PoTnvpQ6uSpR3M63w=
11 | - REQ015: DxAA240XYSKeWMmaKCR3OymyabO52_T7dQGRVfF7yKw=
12 | normative: true
13 | ref: ''
14 | reviewed: yFFQ523SPsaP7IgGKaQ1f-guLTZ0-8msYsaqK-zuxAI=
15 | text: |
16 | These checks ensure the version control system (VCS) meets the needs of
17 | requirements management:
18 |
19 | - Verify the VCS includes a 'tag' feature.
20 | - Verify the VCS stores files in a permanent and secure manner.
21 | - Verify the VCS handles change management of files.
22 | - Verify the VCS associates changes to existing developer acccounts.
23 | - Verify the VCS can manage changes to thousands of files.
24 |
--------------------------------------------------------------------------------
/doorstop/core/tests/docs/LLT008.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 2.2
5 | links:
6 | - REQ015: DxAA240XYSKeWMmaKCR3OymyabO52_T7dQGRVfF7yKw=
7 | normative: true
8 | ref: ''
9 | reviewed: 5HmM-ABqS824omdludZzfX5gPwIH2gEq4l3EYf9wXkY=
10 | text: |
11 | These checks ensure the Python package is distributed properly:
12 |
13 | - Verify the installation can be performed on a new computer in fewer than 10
14 | seconds.
15 |
--------------------------------------------------------------------------------
/doorstop/core/tests/docs/LLT009.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.0
5 | links: []
6 | normative: false
7 | ref: ''
8 | reviewed: kSJLszT3Mu5O-OdoMVqiniUEc1w4voBPiZZJ4bmfBBs=
9 | text: |
10 | Automated Tests
11 |
--------------------------------------------------------------------------------
/doorstop/core/tests/docs/LLT010.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 2.0
5 | links: []
6 | normative: false
7 | ref: ''
8 | reviewed: vAqWkWng0CMNOxNTMWZGUC-l3YfBOIpl1dIxJdUh3wQ=
9 | text: |
10 | Inspection Tests
11 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/.doorstop.skip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/tests/files/.doorstop.skip
--------------------------------------------------------------------------------
/doorstop/core/tests/files/.doorstop.yml:
--------------------------------------------------------------------------------
1 | settings:
2 | digits: 2
3 | parent: SYS
4 | prefix: REQ
5 | sep: ''
6 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/.venv/doorstop/reqs/.doorstop.yml:
--------------------------------------------------------------------------------
1 | settings:
2 | digits: 2
3 | prefix: REQ
4 | sep: ''
5 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/REQ001.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.2.3
5 | links:
6 | - SYS001: null
7 | - SYS002: abc123
8 | normative: true
9 | ref: ''
10 | reviewed: null
11 | text: |
12 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
13 | tempor incididunt ut labore et dolore magna aliqua.
14 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
15 | aliquip ex ea commodo consequat.
16 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
17 | eu fugiat nulla pariatur.
18 | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
19 | deserunt mollit anim id est laborum.
20 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/REQ002.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: |
4 | Plantuml
5 | level: 2.1
6 | links: []
7 | normative: true
8 | ref: ''
9 | reviewed: 1PvBLmy0xmdK_zLKrLu1au0wlIw_zsD6A8Oc5F4zWxU=
10 | text: |
11 | Hello, world!
12 |
13 | ```plantuml format="svg_inline" alt="Use Cases of Doorstop" title="Use Cases of Doorstop"
14 | @startuml
15 | Author --> (Create Document)
16 | Author --> (Create Item)
17 | Author --> (Link Item to Document)
18 | Author --> (Link Item to other Item)
19 | Author --> (Edit Item)
20 | Author --> (Review Item)
21 | Author -> (Delete Item)
22 | Author -> (Delete Document)
23 | (Export) <- (Author)
24 | (Import) <- (Author)
25 | Reviewer --> (Review Item)
26 | System --> (Suspect Changes)
27 | System --> (Integrity)
28 | @enduml
29 | ```
30 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/REQ003.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.4
5 | links:
6 | - REQ001: null
7 | normative: true
8 | ref: REF123
9 | reviewed: null
10 | text: |
11 | Unicode: -40° ±1%
12 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/REQ006.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1.5
5 | links:
6 | - REQ001: 35ed54323e3054c33ae5545fffdbbbf5
7 | normative: true
8 | ref: ''
9 | references:
10 | - keyword: REF123
11 | path: external/text.txt
12 | type: file
13 | - path: external/text2.txt
14 | type: file
15 | reviewed: c442316131ca0225595ae257f3b4583d
16 | text: |
17 | Hello, world!
18 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/REQ2-001.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 2.1
5 | links:
6 | - REQ001: null
7 | normative: true
8 | ref: ''
9 | reviewed: null
10 | text: |
11 | Hello, world!
12 |
13 | Test Math Expressions in Latex Style:
14 |
15 | Inline Style 1: $a \ne 0$
16 | Inline Style 2: \(ax^2 + bx + c = 0\)
17 | Multiline: $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$
18 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/a/b/template.yml:
--------------------------------------------------------------------------------
1 | text: 'Some text'
2 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/a/template.yml:
--------------------------------------------------------------------------------
1 | defaults: !include b/template.yml
2 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/child/.doorstop.skip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/tests/files/child/.doorstop.skip
--------------------------------------------------------------------------------
/doorstop/core/tests/files/child/.doorstop.yml:
--------------------------------------------------------------------------------
1 | settings:
2 | digits: 3
3 | prefix: TST
4 | parent: REQ
5 | sep: ''
6 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/child/TST001.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 1
5 | links:
6 | - REQ002: null
7 | - REQ2-001: null
8 | normative: true
9 | ref: ''
10 | reviewed: null
11 | text: |
12 | Hello, world!
13 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/child/TST002.yml:
--------------------------------------------------------------------------------
1 | active: true
2 | derived: false
3 | header: ''
4 | level: 2
5 | links:
6 | - REQ002: null
7 | normative: true
8 | ref: ''
9 | reviewed: null
10 | text: |
11 | Hello, world!
12 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/exported-huge.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/tests/files/exported-huge.xlsx
--------------------------------------------------------------------------------
/doorstop/core/tests/files/exported-modified.csv:
--------------------------------------------------------------------------------
1 | id,level,text,ref,links,active,derived,normative,additional
2 | REQ0555,1.2.3,"Hello, world!
3 | ",,"SYS001,
4 | SYS002",True,"False",FALSE,
5 | REQ003,1.4,"Hello, world!
6 | ",REF123,REQ001,false,"FALSE",True,"Some ""quoted"" text 'here'."
7 | REQ004,1.6,"Hello, world!
8 | ",,,FAlSe,tRue,"TRUE",
9 | REQ002,2.1,"Hello, world!
10 | ",,,"true",False,"True",""
11 | REQ2-001,2.1,"Hello, world!
12 | ",,REQ001,True,"false",True,""
13 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/exported.csv:
--------------------------------------------------------------------------------
1 | uid,level,text,ref,references,links,active,derived,header,normative,reviewed
2 | REQ001,1.2.3,"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
3 | tempor incididunt ut labore et dolore magna aliqua.
4 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
5 | aliquip ex ea commodo consequat.
6 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
7 | eu fugiat nulla pariatur.
8 | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
9 | deserunt mollit anim id est laborum.",,,"SYS001
10 | SYS002:abc123",True,False,,True,
11 | REQ003,1.4,Unicode: -40° ±1%,REF123,,REQ001,True,False,,True,
12 | REQ006,1.5,"Hello, world!",,"type:file,path:external/text.txt,keyword:REF123
13 | type:file,path:external/text2.txt",REQ001:35ed54323e3054c33ae5545fffdbbbf5,True,False,,True,c442316131ca0225595ae257f3b4583d
14 | REQ004,1.6,"Hello, world!",,,,True,False,,True,
15 | REQ002,2.1,"Hello, world!
16 |
17 | ```plantuml format=""svg_inline"" alt=""Use Cases of Doorstop"" title=""Use Cases of Doorstop""
18 | @startuml
19 | Author --> (Create Document)
20 | Author --> (Create Item)
21 | Author --> (Link Item to Document)
22 | Author --> (Link Item to other Item)
23 | Author --> (Edit Item)
24 | Author --> (Review Item)
25 | Author -> (Delete Item)
26 | Author -> (Delete Document)
27 | (Export) <- (Author)
28 | (Import) <- (Author)
29 | Reviewer --> (Review Item)
30 | System --> (Suspect Changes)
31 | System --> (Integrity)
32 | @enduml
33 | ```",,,,True,False,Plantuml,True,1PvBLmy0xmdK_zLKrLu1au0wlIw_zsD6A8Oc5F4zWxU=
34 | REQ2-001,2.1,"Hello, world!
35 |
36 | Test Math Expressions in Latex Style:
37 |
38 | Inline Style 1: $a \ne 0$
39 | Inline Style 2: \(ax^2 + bx + c = 0\)
40 | Multiline: $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$",,,REQ001,True,False,,True,
41 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/exported.tsv:
--------------------------------------------------------------------------------
1 | uid level text ref references links active derived header normative reviewed
2 | REQ001 1.2.3 "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
3 | tempor incididunt ut labore et dolore magna aliqua.
4 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
5 | aliquip ex ea commodo consequat.
6 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
7 | eu fugiat nulla pariatur.
8 | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
9 | deserunt mollit anim id est laborum." "SYS001
10 | SYS002:abc123" True False True
11 | REQ003 1.4 Unicode: -40° ±1% REF123 REQ001 True False True
12 | REQ006 1.5 Hello, world! "type:file,path:external/text.txt,keyword:REF123
13 | type:file,path:external/text2.txt" REQ001:35ed54323e3054c33ae5545fffdbbbf5 True False True c442316131ca0225595ae257f3b4583d
14 | REQ004 1.6 Hello, world! True False True
15 | REQ002 2.1 "Hello, world!
16 |
17 | ```plantuml format=""svg_inline"" alt=""Use Cases of Doorstop"" title=""Use Cases of Doorstop""
18 | @startuml
19 | Author --> (Create Document)
20 | Author --> (Create Item)
21 | Author --> (Link Item to Document)
22 | Author --> (Link Item to other Item)
23 | Author --> (Edit Item)
24 | Author --> (Review Item)
25 | Author -> (Delete Item)
26 | Author -> (Delete Document)
27 | (Export) <- (Author)
28 | (Import) <- (Author)
29 | Reviewer --> (Review Item)
30 | System --> (Suspect Changes)
31 | System --> (Integrity)
32 | @enduml
33 | ```" True False Plantuml True 1PvBLmy0xmdK_zLKrLu1au0wlIw_zsD6A8Oc5F4zWxU=
34 | REQ2-001 2.1 "Hello, world!
35 |
36 | Test Math Expressions in Latex Style:
37 |
38 | Inline Style 1: $a \ne 0$
39 | Inline Style 2: \(ax^2 + bx + c = 0\)
40 | Multiline: $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$" REQ001 True False True
41 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/exported.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/tests/files/exported.xlsx
--------------------------------------------------------------------------------
/doorstop/core/tests/files/exported.yml:
--------------------------------------------------------------------------------
1 | REQ001:
2 | active: true
3 | derived: false
4 | header: ''
5 | level: 1.2.3
6 | links:
7 | - SYS001: null
8 | - SYS002: abc123
9 | normative: true
10 | ref: ''
11 | reviewed: null
12 | text: |
13 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
14 | tempor incididunt ut labore et dolore magna aliqua.
15 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
16 | aliquip ex ea commodo consequat.
17 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
18 | eu fugiat nulla pariatur.
19 | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
20 | deserunt mollit anim id est laborum.
21 |
22 | REQ003:
23 | active: true
24 | derived: false
25 | header: ''
26 | level: 1.4
27 | links:
28 | - REQ001: null
29 | normative: true
30 | ref: REF123
31 | reviewed: null
32 | text: |
33 | Unicode: -40° ±1%
34 |
35 | REQ006:
36 | active: true
37 | derived: false
38 | header: ''
39 | level: 1.5
40 | links:
41 | - REQ001: 35ed54323e3054c33ae5545fffdbbbf5
42 | normative: true
43 | ref: ''
44 | references:
45 | - keyword: REF123
46 | path: external/text.txt
47 | type: file
48 | - path: external/text2.txt
49 | type: file
50 | reviewed: c442316131ca0225595ae257f3b4583d
51 | text: |
52 | Hello, world!
53 |
54 | REQ004:
55 | active: true
56 | derived: false
57 | header: ''
58 | level: 1.6
59 | links: []
60 | normative: true
61 | ref: ''
62 | reviewed: null
63 | text: |
64 | Hello, world!
65 |
66 | REQ002:
67 | active: true
68 | derived: false
69 | header: |
70 | Plantuml
71 | level: 2.1
72 | links: []
73 | normative: true
74 | ref: ''
75 | reviewed: 1PvBLmy0xmdK_zLKrLu1au0wlIw_zsD6A8Oc5F4zWxU=
76 | text: |
77 | Hello, world!
78 |
79 | ```plantuml format="svg_inline" alt="Use Cases of Doorstop" title="Use Cases of Doorstop"
80 | @startuml
81 | Author --> (Create Document)
82 | Author --> (Create Item)
83 | Author --> (Link Item to Document)
84 | Author --> (Link Item to other Item)
85 | Author --> (Edit Item)
86 | Author --> (Review Item)
87 | Author -> (Delete Item)
88 | Author -> (Delete Document)
89 | (Export) <- (Author)
90 | (Import) <- (Author)
91 | Reviewer --> (Review Item)
92 | System --> (Suspect Changes)
93 | System --> (Integrity)
94 | @enduml
95 | ```
96 |
97 | REQ2-001:
98 | active: true
99 | derived: false
100 | header: ''
101 | level: 2.1
102 | links:
103 | - REQ001: null
104 | normative: true
105 | ref: ''
106 | reviewed: null
107 | text: |
108 | Hello, world!
109 |
110 | Test Math Expressions in Latex Style:
111 |
112 | Inline Style 1: $a \ne 0$
113 | Inline Style 2: \(ax^2 + bx + c = 0\)
114 | Multiline: $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$
115 |
116 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/external/text.txt:
--------------------------------------------------------------------------------
1 | REF122
2 |
3 | REF123
4 |
5 | REF124
6 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/external/text2.txt:
--------------------------------------------------------------------------------
1 | CONTENT HERE IS NOT RELEVANT
2 |
--------------------------------------------------------------------------------
/doorstop/core/tests/files/formula.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doorstop-dev/doorstop/96ebab9689bb906a88190305aad82f715f71af49/doorstop/core/tests/files/formula.xlsx
--------------------------------------------------------------------------------
/doorstop/core/tests/files/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
126 |
127 |
128 |
129 | Published Documents:
130 |
131 |
136 |
137 |
138 |
139 |