hackerrank-cli 2026.10.1.1__tar.gz → 2026.10.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.github/workflows/ci.yml +4 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.github/workflows/release.yml +10 -4
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/MANIFEST.in +4 -2
- {hackerrank_cli-2026.10.1.1/src/hackerrank_cli.egg-info → hackerrank_cli-2026.10.8}/PKG-INFO +32 -5
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/README.rst +31 -4
- hackerrank_cli-2026.10.8/VERSION +1 -0
- hackerrank_cli-2026.10.1.1/CHANGELOG.rst → hackerrank_cli-2026.10.8/docs/source/changelog/2026.10.1.1.md +2 -11
- hackerrank_cli-2026.10.8/docs/source/changelog/2026.10.7.md +7 -0
- hackerrank_cli-2026.10.8/docs/source/changelog/2026.10.8.md +6 -0
- hackerrank_cli-2026.10.8/docs/source/changelog.md +11 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/docs/source/conf.py +1 -1
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/docs/source/development.rst +14 -6
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/docs/source/migration.rst +23 -2
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/prek.toml +2 -2
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/pyproject.toml +12 -7
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli/__init__.py +84 -29
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli/_version.py +3 -3
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8/src/hackerrank_cli.egg-info}/PKG-INFO +32 -5
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli.egg-info/SOURCES.txt +6 -2
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli.egg-info/scm_file_list.json +6 -2
- hackerrank_cli-2026.10.8/src/hackerrank_cli.egg-info/scm_version.json +8 -0
- hackerrank_cli-2026.10.8/tests/test_archive.py +145 -0
- hackerrank_cli-2026.10.8/tests/test_help/test_help_archive_.txt +11 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/tests/test_help/test_help_questions_.txt +2 -1
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/tests/test_help.py +7 -2
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/uv.lock +55 -37
- hackerrank_cli-2026.10.1.1/VERSION +0 -1
- hackerrank_cli-2026.10.1.1/docs/source/changelog.rst +0 -1
- hackerrank_cli-2026.10.1.1/src/hackerrank_cli.egg-info/scm_version.json +0 -8
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.dockerignore +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.gitattributes +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.github/dependabot.yml +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.github/workflows/autofix.yml +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.github/workflows/binaries.yml +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.github/workflows/dependabot-merge.yml +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.github/workflows/docs.yml +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.github/workflows/lint.yml +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.github/workflows/nix.yml +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.github/workflows/publish-site.yml +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.gitignore +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/.vale.ini +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/Dockerfile +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/LICENSE +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/bin/__init__.py +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/bin/hackerrank_wrapper.py +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/docs/__init__.py +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/docs/source/__init__.py +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/docs/source/cli.rst +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/docs/source/index.rst +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/docs/source/macos-releases.rst +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/docs/source/usage.rst +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/flake.lock +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/flake.nix +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/newsfragments/.gitkeep +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/setup.cfg +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/spelling_private_dict.txt +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli/__main__.py +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli/_sources.py +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli/py.typed +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli.egg-info/dependency_links.txt +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli.egg-info/entry_points.txt +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli.egg-info/not-zip-safe +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli.egg-info/requires.txt +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli.egg-info/top_level.txt +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/styles/snapper/LongProseLine.yml +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/styles/snapper/SemanticLineBreaks.yml +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/tests/__init__.py +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/tests/test_cli.py +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/tests/test_help/test_help_root_.txt +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/tests/test_help/test_help_upload_.txt +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/tests/test_http.py +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/tests/test_sources.py +0 -0
- {hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/zizmor.yml +0 -0
|
@@ -44,6 +44,10 @@ jobs:
|
|
|
44
44
|
- run: uv build
|
|
45
45
|
- run: uv run --locked twine check dist/*
|
|
46
46
|
- run: uv run --locked check-wheel-contents dist/*.whl
|
|
47
|
+
- name: Check release notes exist
|
|
48
|
+
run: |
|
|
49
|
+
RELEASE_VERSION="$(cat VERSION)"
|
|
50
|
+
test -s "docs/source/changelog/$RELEASE_VERSION.md"
|
|
47
51
|
- name: Smoke installed wheel
|
|
48
52
|
run: |
|
|
49
53
|
uv venv /tmp/hackerrank-wheel
|
|
@@ -27,10 +27,18 @@ jobs:
|
|
|
27
27
|
- run: uv build
|
|
28
28
|
- run: uv run --locked twine check dist/*
|
|
29
29
|
- run: uv run --locked check-wheel-contents dist/*.whl
|
|
30
|
+
- name: Select the tagged release notes
|
|
31
|
+
env:
|
|
32
|
+
RELEASE_VERSION: ${{ github.ref_name }}
|
|
33
|
+
run: cp "docs/source/changelog/$RELEASE_VERSION.md" release-notes.md
|
|
30
34
|
- uses: actions/upload-artifact@v7
|
|
31
35
|
with:
|
|
32
36
|
name: distributions
|
|
33
37
|
path: dist/*
|
|
38
|
+
- uses: actions/upload-artifact@v7
|
|
39
|
+
with:
|
|
40
|
+
name: release-notes
|
|
41
|
+
path: release-notes.md
|
|
34
42
|
binaries:
|
|
35
43
|
# actionlint 1.7.12 accepts ./ for local workflows and rejects $/.
|
|
36
44
|
uses: ./.github/workflows/binaries.yml # zizmor: ignore[self-repository]
|
|
@@ -60,18 +68,16 @@ jobs:
|
|
|
60
68
|
permissions:
|
|
61
69
|
contents: write
|
|
62
70
|
steps:
|
|
63
|
-
- uses: actions/checkout@v7
|
|
64
|
-
with:
|
|
65
|
-
persist-credentials: false
|
|
66
71
|
- uses: actions/download-artifact@v8
|
|
67
72
|
with:
|
|
68
73
|
path: artifacts
|
|
69
74
|
- name: Attach distributions and binaries
|
|
70
75
|
env:
|
|
76
|
+
GH_REPO: ${{ github.repository }}
|
|
71
77
|
GH_TOKEN: ${{ github.token }}
|
|
72
78
|
TAG: ${{ github.ref_name }}
|
|
73
79
|
run: gh release create "$TAG" artifacts/distributions/* artifacts/hackerrank-*/*
|
|
74
|
-
--title "$TAG" --notes-file
|
|
80
|
+
--title "$TAG" --notes-file artifacts/release-notes/release-notes.md --verify-tag
|
|
75
81
|
docker:
|
|
76
82
|
needs: publish
|
|
77
83
|
runs-on: ubuntu-latest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
include LICENSE README.rst
|
|
1
|
+
include LICENSE README.rst
|
|
2
2
|
recursive-include src *.py *.typed
|
|
3
|
-
recursive-include docs *.rst *.py
|
|
3
|
+
recursive-include docs *.rst *.md *.py
|
|
4
4
|
recursive-include newsfragments *
|
|
5
5
|
include .vale.ini prek.toml spelling_private_dict.txt zizmor.yml
|
|
6
6
|
recursive-include styles/snapper *.yml
|
|
@@ -11,3 +11,5 @@ recursive-include bin *.py
|
|
|
11
11
|
recursive-include tests *.py *.txt
|
|
12
12
|
include .dockerignore
|
|
13
13
|
include .gitattributes
|
|
14
|
+
|
|
15
|
+
include VERSION
|
{hackerrank_cli-2026.10.1.1/src/hackerrank_cli.egg-info → hackerrank_cli-2026.10.8}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hackerrank-cli
|
|
3
|
-
Version: 2026.10.
|
|
3
|
+
Version: 2026.10.8
|
|
4
4
|
Summary: Upload starter files to existing HackerRank project questions.
|
|
5
5
|
Author-email: Adam Dangoor <adamdangoor@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -44,9 +44,16 @@ Install from PyPI:
|
|
|
44
44
|
|
|
45
45
|
uv tool install hackerrank-cli
|
|
46
46
|
hackerrank --help
|
|
47
|
-
hackerrank --version
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
Or install the checkout for development:
|
|
49
|
+
|
|
50
|
+
.. code-block:: console
|
|
51
|
+
|
|
52
|
+
uv sync --locked
|
|
53
|
+
uv run hackerrank --help
|
|
54
|
+
uv run python -m hackerrank_cli --version
|
|
55
|
+
|
|
56
|
+
``pip install hackerrank-cli`` is also supported.
|
|
50
57
|
|
|
51
58
|
Usage
|
|
52
59
|
-----
|
|
@@ -86,6 +93,27 @@ repeatable upload.
|
|
|
86
93
|
The CLI adds no separate retry loop.
|
|
87
94
|
Each attempt uses the same prepared archive bytes.
|
|
88
95
|
|
|
96
|
+
Offline archives
|
|
97
|
+
----------------
|
|
98
|
+
|
|
99
|
+
Export the same project ZIP locally without a question ID, credentials, or
|
|
100
|
+
network access:
|
|
101
|
+
|
|
102
|
+
.. code-block:: console
|
|
103
|
+
|
|
104
|
+
hackerrank questions archive --directory ./starter --exclude '*.zip' --output ./starter.zip
|
|
105
|
+
hackerrank questions archive --file ./starter.py --output - > ./starter.zip
|
|
106
|
+
|
|
107
|
+
``--output`` is required.
|
|
108
|
+
A file destination must not already exist; ``-`` writes only ZIP bytes to
|
|
109
|
+
standard output.
|
|
110
|
+
Shell redirection controls whether its destination is overwritten.
|
|
111
|
+
Source validation and packaging finish before the output is opened.
|
|
112
|
+
The source options, ignore rules, file contents, and executable permissions
|
|
113
|
+
match ``questions upload``.
|
|
114
|
+
Consumers can extract this archive for local checks, then send its bytes with
|
|
115
|
+
the SDK's ``questions.upload_project_zip`` method.
|
|
116
|
+
|
|
89
117
|
Directory selection
|
|
90
118
|
-------------------
|
|
91
119
|
|
|
@@ -136,8 +164,7 @@ Development and distribution
|
|
|
136
164
|
Development checks, release setup, and build instructions for Docker, Nix, and
|
|
137
165
|
standalone binaries are in ``docs/source/development.rst``.
|
|
138
166
|
The generated CLI reference is in ``docs/source/cli.rst``.
|
|
139
|
-
|
|
140
|
-
`GitHub Releases <https://github.com/adamtheturtle/hackerrank-cli/releases>`_.
|
|
167
|
+
There are no prebuilt artifacts yet.
|
|
141
168
|
|
|
142
169
|
See ``docs/source/migration.rst`` for migrating an existing shell uploader and
|
|
143
170
|
keeping repository-specific snippet preparation outside this CLI.
|
|
@@ -18,9 +18,16 @@ Install from PyPI:
|
|
|
18
18
|
|
|
19
19
|
uv tool install hackerrank-cli
|
|
20
20
|
hackerrank --help
|
|
21
|
-
hackerrank --version
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
Or install the checkout for development:
|
|
23
|
+
|
|
24
|
+
.. code-block:: console
|
|
25
|
+
|
|
26
|
+
uv sync --locked
|
|
27
|
+
uv run hackerrank --help
|
|
28
|
+
uv run python -m hackerrank_cli --version
|
|
29
|
+
|
|
30
|
+
``pip install hackerrank-cli`` is also supported.
|
|
24
31
|
|
|
25
32
|
Usage
|
|
26
33
|
-----
|
|
@@ -60,6 +67,27 @@ repeatable upload.
|
|
|
60
67
|
The CLI adds no separate retry loop.
|
|
61
68
|
Each attempt uses the same prepared archive bytes.
|
|
62
69
|
|
|
70
|
+
Offline archives
|
|
71
|
+
----------------
|
|
72
|
+
|
|
73
|
+
Export the same project ZIP locally without a question ID, credentials, or
|
|
74
|
+
network access:
|
|
75
|
+
|
|
76
|
+
.. code-block:: console
|
|
77
|
+
|
|
78
|
+
hackerrank questions archive --directory ./starter --exclude '*.zip' --output ./starter.zip
|
|
79
|
+
hackerrank questions archive --file ./starter.py --output - > ./starter.zip
|
|
80
|
+
|
|
81
|
+
``--output`` is required.
|
|
82
|
+
A file destination must not already exist; ``-`` writes only ZIP bytes to
|
|
83
|
+
standard output.
|
|
84
|
+
Shell redirection controls whether its destination is overwritten.
|
|
85
|
+
Source validation and packaging finish before the output is opened.
|
|
86
|
+
The source options, ignore rules, file contents, and executable permissions
|
|
87
|
+
match ``questions upload``.
|
|
88
|
+
Consumers can extract this archive for local checks, then send its bytes with
|
|
89
|
+
the SDK's ``questions.upload_project_zip`` method.
|
|
90
|
+
|
|
63
91
|
Directory selection
|
|
64
92
|
-------------------
|
|
65
93
|
|
|
@@ -110,8 +138,7 @@ Development and distribution
|
|
|
110
138
|
Development checks, release setup, and build instructions for Docker, Nix, and
|
|
111
139
|
standalone binaries are in ``docs/source/development.rst``.
|
|
112
140
|
The generated CLI reference is in ``docs/source/cli.rst``.
|
|
113
|
-
|
|
114
|
-
`GitHub Releases <https://github.com/adamtheturtle/hackerrank-cli/releases>`_.
|
|
141
|
+
There are no prebuilt artifacts yet.
|
|
115
142
|
|
|
116
143
|
See ``docs/source/migration.rst`` for migrating an existing shell uploader and
|
|
117
144
|
keeping repository-specific snippet preparation outside this CLI.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2026.10.8
|
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
=========
|
|
1
|
+
# 2026.10.1.1 (2026-10-01)
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
.. towncrier release notes start
|
|
7
|
-
|
|
8
|
-
2026.10.1.1 (2026-10-01)
|
|
9
|
-
------------------------
|
|
10
|
-
|
|
11
|
-
Features
|
|
12
|
-
~~~~~~~~
|
|
3
|
+
## Features
|
|
13
4
|
|
|
14
5
|
- Upload existing HackerRank project-question starter files from a directory or
|
|
15
6
|
a single file, with nested ignore rules, configurable exclusions, offline dry
|
|
@@ -5,7 +5,7 @@ import importlib.metadata
|
|
|
5
5
|
project = "hackerrank-cli"
|
|
6
6
|
author = "Adam Dangoor"
|
|
7
7
|
release = importlib.metadata.version(distribution_name="hackerrank-cli")
|
|
8
|
-
extensions = ["sphinx_click.ext", "sphinxcontrib.spelling"]
|
|
8
|
+
extensions = ["myst_parser", "sphinx_click.ext", "sphinxcontrib.spelling"]
|
|
9
9
|
html_theme = "furo"
|
|
10
10
|
nitpicky = True
|
|
11
11
|
html_theme_options = {
|
|
@@ -40,19 +40,27 @@ The upload tests so assert the released SDK's actual wire behavior instead of
|
|
|
40
40
|
inventing an OpenAPI schema.
|
|
41
41
|
No separate mock-spec repository is needed for this scope.
|
|
42
42
|
|
|
43
|
-
Add a Towncrier feature or bugfix fragment for each user-visible
|
|
43
|
+
Add a Towncrier feature or bugfix fragment in Markdown for each user-visible
|
|
44
|
+
change, such as ``newsfragments/123.bugfix.md``.
|
|
44
45
|
|
|
45
46
|
Versions derive from Git tags using setuptools-scm.
|
|
46
|
-
Untagged
|
|
47
|
-
Use date-based tags such as ``2026.10.
|
|
48
|
-
created as part of repository initialization.
|
|
47
|
+
Untagged builds have a development version.
|
|
48
|
+
Use date-based tags such as ``2026.10.7`` for releases.
|
|
49
49
|
Before tagging a release, assemble notes with
|
|
50
|
-
``uv run towncrier build --yes --version VERSION``, commit the
|
|
51
|
-
|
|
50
|
+
``uv run towncrier build --yes --version VERSION``, commit the generated notes
|
|
51
|
+
in ``docs/source/changelog/VERSION.md``, then write the same version to
|
|
52
|
+
``VERSION``, commit it, then tag the commit.
|
|
52
53
|
Pushing a tag is the explicit publication trigger.
|
|
53
54
|
The release workflow builds and checks packages, builds three standalone
|
|
54
55
|
binaries, and then publishes to PyPI, GitHub Releases, and GHCR.
|
|
55
56
|
|
|
57
|
+
Towncrier writes a separate Markdown file for each release.
|
|
58
|
+
Sphinx reads those files through MyST and lists them in the changelog.
|
|
59
|
+
The release workflow uploads the tagged version's file directly as its GitHub
|
|
60
|
+
release notes.
|
|
61
|
+
Pull requests check that the notes for ``VERSION`` exist.
|
|
62
|
+
Review the generated Markdown before pushing a release tag.
|
|
63
|
+
|
|
56
64
|
Local builds
|
|
57
65
|
------------
|
|
58
66
|
|
|
@@ -7,8 +7,7 @@ repository that owns the questions.
|
|
|
7
7
|
This CLI uploads starter files to an existing project question.
|
|
8
8
|
It never creates questions or updates metadata.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
repository.
|
|
10
|
+
Pin a released ``hackerrank-cli`` version in that repository.
|
|
12
11
|
Replace each project archive upload with:
|
|
13
12
|
|
|
14
13
|
.. code-block:: shell
|
|
@@ -27,3 +26,25 @@ The default is zero, matching the released SDK.
|
|
|
27
26
|
Uploads use immutable archive bytes so retries send the same content.
|
|
28
27
|
Do not enable live uploads until the file list and project behavior have been
|
|
29
28
|
reviewed in the owning repository.
|
|
29
|
+
|
|
30
|
+
Local validation
|
|
31
|
+
----------------
|
|
32
|
+
|
|
33
|
+
Use ``hackerrank questions archive`` when local checks need the exact project
|
|
34
|
+
layout candidates receive:
|
|
35
|
+
|
|
36
|
+
.. code-block:: shell
|
|
37
|
+
|
|
38
|
+
hackerrank questions archive --directory ./starter --exclude '*.zip' --output ./starter.zip
|
|
39
|
+
|
|
40
|
+
Extract the archive in a temporary directory to run project checks.
|
|
41
|
+
An uploader that already uses the SDK can send the exported bytes with
|
|
42
|
+
``questions.upload_project_zip`` instead of maintaining another ZIP builder.
|
|
43
|
+
The export command shares source selection and packaging with CLI uploads.
|
|
44
|
+
It makes no network requests and requires no API token.
|
|
45
|
+
|
|
46
|
+
Gitignore patterns match directories as well as files.
|
|
47
|
+
When translating a file-only exclusion such as ``app``, add ``!app/`` after it
|
|
48
|
+
if source directories of that name must remain included.
|
|
49
|
+
Inherited ``.gitignore`` rules still apply, so compare the exported file list
|
|
50
|
+
and contents before replacing an existing packager.
|
|
@@ -597,7 +597,7 @@ hooks = [
|
|
|
597
597
|
name = "linkcheck",
|
|
598
598
|
entry = "uv run --locked --group=dev sphinx-build -M linkcheck docs/source docs/build -W --keep-going -d docs/build/.doctrees-linkcheck",
|
|
599
599
|
language = "system",
|
|
600
|
-
types_or = ["rst"],
|
|
600
|
+
types_or = ["rst", "markdown"],
|
|
601
601
|
stages = ["manual"],
|
|
602
602
|
groups = ["docs"],
|
|
603
603
|
priority = 610,
|
|
@@ -609,7 +609,7 @@ hooks = [
|
|
|
609
609
|
name = "spelling",
|
|
610
610
|
entry = "uv run --locked --group=dev sphinx-build -M spelling docs/source docs/build -W -d docs/build/.doctrees-spelling",
|
|
611
611
|
language = "system",
|
|
612
|
-
types_or = ["rst"],
|
|
612
|
+
types_or = ["rst", "markdown"],
|
|
613
613
|
stages = ["manual"],
|
|
614
614
|
groups = ["docs"],
|
|
615
615
|
priority = 610,
|
|
@@ -37,8 +37,10 @@ dev = [
|
|
|
37
37
|
"doccmd==2026.9.16.1",
|
|
38
38
|
"furo==2025.12.19",
|
|
39
39
|
"interrogate==1.7.0",
|
|
40
|
-
"mypy==2.
|
|
40
|
+
"mypy==2.4.0",
|
|
41
|
+
"mypy-pytest-parametrize==2026.10.5",
|
|
41
42
|
"mypy-strict-kwargs==2026.8.25.1",
|
|
43
|
+
"myst-parser==5.1.0",
|
|
42
44
|
"no-defaults==2026.9.1",
|
|
43
45
|
"prek==0.5.4",
|
|
44
46
|
"pydocstringformatter==1.0.0",
|
|
@@ -198,7 +200,8 @@ exclude_gitignore = true
|
|
|
198
200
|
follow_untyped_imports = true
|
|
199
201
|
python_version = "3.13"
|
|
200
202
|
strict = true
|
|
201
|
-
plugins = [ "mypy_strict_kwargs" ]
|
|
203
|
+
plugins = [ "mypy_pytest_parametrize", "mypy_strict_kwargs" ]
|
|
204
|
+
num_workers = "auto"
|
|
202
205
|
|
|
203
206
|
[tool.pyrefly]
|
|
204
207
|
project-excludes = [ "src/hackerrank_cli/_version.py" ]
|
|
@@ -224,23 +227,25 @@ analysis.strict-generic-narrowing = true
|
|
|
224
227
|
|
|
225
228
|
[tool.pytest]
|
|
226
229
|
ini_options.testpaths = [ "tests" ]
|
|
227
|
-
ini_options.addopts = "-n auto --cov
|
|
230
|
+
ini_options.addopts = "-n auto --cov --cov-config=pyproject.toml"
|
|
228
231
|
ini_options.xfail_strict = true
|
|
229
232
|
ini_options.beartype_packages = [ "hackerrank_cli" ]
|
|
230
233
|
ini_options.beartype_test_fixtures = true
|
|
231
234
|
ini_options.beartype_tests = true
|
|
232
235
|
|
|
233
236
|
[tool.coverage]
|
|
237
|
+
run.source = [ "hackerrank_cli" ]
|
|
234
238
|
run.omit = [ "src/hackerrank_cli/__main__.py", "src/hackerrank_cli/_version.py" ]
|
|
235
239
|
run.branch = true
|
|
240
|
+
report.fail_under = 100
|
|
236
241
|
report.show_missing = true
|
|
237
242
|
|
|
238
243
|
[tool.towncrier]
|
|
239
244
|
directory = "newsfragments"
|
|
240
|
-
filename = "
|
|
241
|
-
title_format = "{version} ({project_date})"
|
|
242
|
-
issue_format = "#{issue}"
|
|
243
|
-
|
|
245
|
+
filename = "docs/source/changelog/{version}.md"
|
|
246
|
+
title_format = "# {version} ({project_date})"
|
|
247
|
+
issue_format = "[#{issue}](https://github.com/adamtheturtle/hackerrank-cli/issues/{issue})"
|
|
248
|
+
single_file = false
|
|
244
249
|
ignore = [ ".gitkeep" ]
|
|
245
250
|
type = [
|
|
246
251
|
{ directory = "feature", name = "Features", showcontent = true },
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Upload project starter code using the released HackerRank SDK."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
from collections.abc import Generator
|
|
5
|
+
from contextlib import contextmanager
|
|
4
6
|
from importlib.metadata import version
|
|
5
7
|
from io import BytesIO
|
|
6
8
|
from pathlib import Path
|
|
@@ -84,27 +86,65 @@ def create_cli(*, transport: Transport | None = None) -> click.Group:
|
|
|
84
86
|
transport=transport,
|
|
85
87
|
)
|
|
86
88
|
|
|
89
|
+
@click.option(
|
|
90
|
+
"--directory",
|
|
91
|
+
type=click.Path(path_type=Path, readable=False),
|
|
92
|
+
help="Project directory.",
|
|
93
|
+
)
|
|
94
|
+
@click.option(
|
|
95
|
+
"--file",
|
|
96
|
+
"source_file",
|
|
97
|
+
type=click.Path(path_type=Path, readable=False),
|
|
98
|
+
help="One starter file, packaged as a single-file project archive.",
|
|
99
|
+
)
|
|
100
|
+
@click.option(
|
|
101
|
+
"--exclude",
|
|
102
|
+
multiple=True,
|
|
103
|
+
help="Additional Gitignore pattern at the upload root. Repeatable.",
|
|
104
|
+
)
|
|
105
|
+
@click.option(
|
|
106
|
+
"--output",
|
|
107
|
+
type=click.Path(dir_okay=False, allow_dash=True),
|
|
108
|
+
required=True,
|
|
109
|
+
help=(
|
|
110
|
+
"New ZIP file, or - for binary standard output. "
|
|
111
|
+
"Never overwrites files."
|
|
112
|
+
),
|
|
113
|
+
)
|
|
114
|
+
def archive(
|
|
115
|
+
directory: Path | None,
|
|
116
|
+
source_file: Path | None,
|
|
117
|
+
exclude: tuple[str, ...],
|
|
118
|
+
output: str,
|
|
119
|
+
) -> None:
|
|
120
|
+
"""Export a project ZIP without credentials or network access."""
|
|
121
|
+
_validate_source(
|
|
122
|
+
directory=directory, source_file=source_file, exclude=exclude
|
|
123
|
+
)
|
|
124
|
+
with (
|
|
125
|
+
_preparation_errors(),
|
|
126
|
+
prepare_source(
|
|
127
|
+
directory=directory, file=source_file, excludes=exclude
|
|
128
|
+
) as source,
|
|
129
|
+
):
|
|
130
|
+
contents = _project_zip(source=source)
|
|
131
|
+
with click.open_file(filename=output, mode="xb") as destination:
|
|
132
|
+
_ = destination.write(contents)
|
|
133
|
+
|
|
134
|
+
_ = questions.command()(archive)
|
|
87
135
|
_ = questions.command()(upload)
|
|
88
136
|
return cli
|
|
89
137
|
|
|
90
138
|
|
|
91
|
-
def
|
|
92
|
-
question_id: str,
|
|
139
|
+
def _validate_source(
|
|
93
140
|
directory: Path | None,
|
|
94
141
|
source_file: Path | None,
|
|
95
142
|
exclude: tuple[str, ...],
|
|
96
143
|
) -> None:
|
|
97
|
-
"""Validate
|
|
144
|
+
"""Validate the shared archive and upload source options."""
|
|
98
145
|
if (directory is None) == (source_file is None):
|
|
99
146
|
msg = "Provide exactly one of --directory or --file."
|
|
100
147
|
raise click.UsageError(message=msg)
|
|
101
|
-
if (
|
|
102
|
-
not question_id.isascii()
|
|
103
|
-
or not question_id.isdecimal()
|
|
104
|
-
or int(question_id) < 1
|
|
105
|
-
):
|
|
106
|
-
msg = "QUESTION_ID must be a positive decimal integer."
|
|
107
|
-
raise click.BadParameter(message=msg, param_hint="QUESTION_ID")
|
|
108
148
|
if len(exclude) > 0 and directory is None:
|
|
109
149
|
msg = "--exclude requires --directory."
|
|
110
150
|
raise click.UsageError(message=msg)
|
|
@@ -187,30 +227,45 @@ def _upload( # noqa: PLR0913 - Click options plus the SDK transport boundary.
|
|
|
187
227
|
transport: Transport | None,
|
|
188
228
|
) -> None:
|
|
189
229
|
"""Prepare every byte before credentials or a network mutation."""
|
|
190
|
-
|
|
191
|
-
question_id=question_id,
|
|
230
|
+
_validate_source(
|
|
192
231
|
directory=directory,
|
|
193
232
|
source_file=source_file,
|
|
194
233
|
exclude=exclude,
|
|
195
234
|
)
|
|
196
|
-
|
|
197
|
-
|
|
235
|
+
if (
|
|
236
|
+
not question_id.isascii()
|
|
237
|
+
or not question_id.isdecimal()
|
|
238
|
+
or int(question_id) < 1
|
|
239
|
+
):
|
|
240
|
+
msg = "QUESTION_ID must be a positive decimal integer."
|
|
241
|
+
raise click.BadParameter(message=msg, param_hint="QUESTION_ID")
|
|
242
|
+
with (
|
|
243
|
+
_preparation_errors(),
|
|
244
|
+
prepare_source(
|
|
198
245
|
directory=directory, file=source_file, excludes=exclude
|
|
199
|
-
) as source
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
246
|
+
) as source,
|
|
247
|
+
):
|
|
248
|
+
archive = _project_zip(source=source)
|
|
249
|
+
target = f"HackerRank project question {question_id}"
|
|
250
|
+
if dry_run:
|
|
251
|
+
click.echo(message=f"Would update {target}")
|
|
252
|
+
for path in source.files:
|
|
253
|
+
click.echo(message=f" {path}")
|
|
254
|
+
return
|
|
255
|
+
_send_upload(
|
|
256
|
+
question_id=question_id,
|
|
257
|
+
archive=archive,
|
|
258
|
+
retries=retries,
|
|
259
|
+
transport=transport,
|
|
260
|
+
)
|
|
261
|
+
click.echo(message=f"Updated {target}")
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@contextmanager
|
|
265
|
+
def _preparation_errors() -> Generator[None]:
|
|
266
|
+
"""Report source and archive errors consistently for both commands."""
|
|
267
|
+
try:
|
|
268
|
+
yield
|
|
214
269
|
except UnicodeError:
|
|
215
270
|
msg = ".gitignore files must be valid UTF-8."
|
|
216
271
|
raise click.ClickException(message=msg) from None
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2026.10.
|
|
22
|
-
__version_tuple__ = version_tuple = (2026, 10,
|
|
21
|
+
__version__ = version = '2026.10.8'
|
|
22
|
+
__version_tuple__ = version_tuple = (2026, 10, 8)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'gfb4c14cb0'
|
{hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8/src/hackerrank_cli.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hackerrank-cli
|
|
3
|
-
Version: 2026.10.
|
|
3
|
+
Version: 2026.10.8
|
|
4
4
|
Summary: Upload starter files to existing HackerRank project questions.
|
|
5
5
|
Author-email: Adam Dangoor <adamdangoor@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -44,9 +44,16 @@ Install from PyPI:
|
|
|
44
44
|
|
|
45
45
|
uv tool install hackerrank-cli
|
|
46
46
|
hackerrank --help
|
|
47
|
-
hackerrank --version
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
Or install the checkout for development:
|
|
49
|
+
|
|
50
|
+
.. code-block:: console
|
|
51
|
+
|
|
52
|
+
uv sync --locked
|
|
53
|
+
uv run hackerrank --help
|
|
54
|
+
uv run python -m hackerrank_cli --version
|
|
55
|
+
|
|
56
|
+
``pip install hackerrank-cli`` is also supported.
|
|
50
57
|
|
|
51
58
|
Usage
|
|
52
59
|
-----
|
|
@@ -86,6 +93,27 @@ repeatable upload.
|
|
|
86
93
|
The CLI adds no separate retry loop.
|
|
87
94
|
Each attempt uses the same prepared archive bytes.
|
|
88
95
|
|
|
96
|
+
Offline archives
|
|
97
|
+
----------------
|
|
98
|
+
|
|
99
|
+
Export the same project ZIP locally without a question ID, credentials, or
|
|
100
|
+
network access:
|
|
101
|
+
|
|
102
|
+
.. code-block:: console
|
|
103
|
+
|
|
104
|
+
hackerrank questions archive --directory ./starter --exclude '*.zip' --output ./starter.zip
|
|
105
|
+
hackerrank questions archive --file ./starter.py --output - > ./starter.zip
|
|
106
|
+
|
|
107
|
+
``--output`` is required.
|
|
108
|
+
A file destination must not already exist; ``-`` writes only ZIP bytes to
|
|
109
|
+
standard output.
|
|
110
|
+
Shell redirection controls whether its destination is overwritten.
|
|
111
|
+
Source validation and packaging finish before the output is opened.
|
|
112
|
+
The source options, ignore rules, file contents, and executable permissions
|
|
113
|
+
match ``questions upload``.
|
|
114
|
+
Consumers can extract this archive for local checks, then send its bytes with
|
|
115
|
+
the SDK's ``questions.upload_project_zip`` method.
|
|
116
|
+
|
|
89
117
|
Directory selection
|
|
90
118
|
-------------------
|
|
91
119
|
|
|
@@ -136,8 +164,7 @@ Development and distribution
|
|
|
136
164
|
Development checks, release setup, and build instructions for Docker, Nix, and
|
|
137
165
|
standalone binaries are in ``docs/source/development.rst``.
|
|
138
166
|
The generated CLI reference is in ``docs/source/cli.rst``.
|
|
139
|
-
|
|
140
|
-
`GitHub Releases <https://github.com/adamtheturtle/hackerrank-cli/releases>`_.
|
|
167
|
+
There are no prebuilt artifacts yet.
|
|
141
168
|
|
|
142
169
|
See ``docs/source/migration.rst`` for migrating an existing shell uploader and
|
|
143
170
|
keeping repository-specific snippet preparation outside this CLI.
|
{hackerrank_cli-2026.10.1.1 → hackerrank_cli-2026.10.8}/src/hackerrank_cli.egg-info/SOURCES.txt
RENAMED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
.gitattributes
|
|
3
3
|
.gitignore
|
|
4
4
|
.vale.ini
|
|
5
|
-
CHANGELOG.rst
|
|
6
5
|
Dockerfile
|
|
7
6
|
LICENSE
|
|
8
7
|
MANIFEST.in
|
|
@@ -29,7 +28,7 @@ bin/__init__.py
|
|
|
29
28
|
bin/hackerrank_wrapper.py
|
|
30
29
|
docs/__init__.py
|
|
31
30
|
docs/source/__init__.py
|
|
32
|
-
docs/source/changelog.
|
|
31
|
+
docs/source/changelog.md
|
|
33
32
|
docs/source/cli.rst
|
|
34
33
|
docs/source/conf.py
|
|
35
34
|
docs/source/development.rst
|
|
@@ -37,6 +36,9 @@ docs/source/index.rst
|
|
|
37
36
|
docs/source/macos-releases.rst
|
|
38
37
|
docs/source/migration.rst
|
|
39
38
|
docs/source/usage.rst
|
|
39
|
+
docs/source/changelog/2026.10.1.1.md
|
|
40
|
+
docs/source/changelog/2026.10.7.md
|
|
41
|
+
docs/source/changelog/2026.10.8.md
|
|
40
42
|
newsfragments/.gitkeep
|
|
41
43
|
src/hackerrank_cli/__init__.py
|
|
42
44
|
src/hackerrank_cli/__main__.py
|
|
@@ -55,10 +57,12 @@ src/hackerrank_cli.egg-info/top_level.txt
|
|
|
55
57
|
styles/snapper/LongProseLine.yml
|
|
56
58
|
styles/snapper/SemanticLineBreaks.yml
|
|
57
59
|
tests/__init__.py
|
|
60
|
+
tests/test_archive.py
|
|
58
61
|
tests/test_cli.py
|
|
59
62
|
tests/test_help.py
|
|
60
63
|
tests/test_http.py
|
|
61
64
|
tests/test_sources.py
|
|
65
|
+
tests/test_help/test_help_archive_.txt
|
|
62
66
|
tests/test_help/test_help_questions_.txt
|
|
63
67
|
tests/test_help/test_help_root_.txt
|
|
64
68
|
tests/test_help/test_help_upload_.txt
|