sshscript 3.1.5__tar.gz → 4.0.2__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.
Files changed (122) hide show
  1. sshscript-4.0.2/.github/ISSUE_TEMPLATE/bug_report.yml +109 -0
  2. sshscript-4.0.2/.github/ISSUE_TEMPLATE/config.yml +11 -0
  3. sshscript-4.0.2/.github/ISSUE_TEMPLATE/feature_request.yml +57 -0
  4. sshscript-4.0.2/.github/pull_request_template.md +32 -0
  5. {sshscript-3.1.5 → sshscript-4.0.2}/.github/workflows/ci.yml +66 -7
  6. {sshscript-3.1.5 → sshscript-4.0.2}/.github/workflows/release.yml +30 -5
  7. sshscript-4.0.2/.gitignore +121 -0
  8. sshscript-4.0.2/API_GUIDE.md +631 -0
  9. sshscript-4.0.2/CHANGELOG.md +276 -0
  10. {sshscript-3.1.5 → sshscript-4.0.2}/CONTRIBUTING.md +2 -1
  11. sshscript-4.0.2/EXCEPTIONS.md +145 -0
  12. sshscript-4.0.2/MANIFEST.in +56 -0
  13. {sshscript-3.1.5 → sshscript-4.0.2}/PKG-INFO +214 -36
  14. {sshscript-3.1.5 → sshscript-4.0.2}/README.md +213 -35
  15. sshscript-4.0.2/RELEASING.md +63 -0
  16. {sshscript-3.1.5 → sshscript-4.0.2}/SECURITY.md +12 -5
  17. sshscript-4.0.2/VERSIONING.md +109 -0
  18. {sshscript-3.1.5 → sshscript-4.0.2}/pyproject.toml +5 -1
  19. sshscript-4.0.2/src/_sshscript_cli.py +19 -0
  20. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/__init__.py +13 -0
  21. sshscript-4.0.2/src/sshscript/__init__.pyi +15 -0
  22. sshscript-4.0.2/src/sshscript/_runtime.py +14 -0
  23. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/_version.py +1 -1
  24. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/channelgeneric.py +48 -16
  25. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/channelssh.py +8 -7
  26. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/channelsubprocess.py +6 -6
  27. sshscript-4.0.2/src/sshscript/channelutils.py +715 -0
  28. sshscript-4.0.2/src/sshscript/commandjob.py +475 -0
  29. sshscript-4.0.2/src/sshscript/commandjob.pyi +45 -0
  30. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/commandresult.py +5 -2
  31. sshscript-4.0.2/src/sshscript/commandresult.pyi +18 -0
  32. sshscript-4.0.2/src/sshscript/consolejob.py +309 -0
  33. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/dollar.py +64 -33
  34. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/dollarchanger.py +14 -2
  35. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/patching.py +3 -1
  36. sshscript-4.0.2/src/sshscript/py.typed +0 -0
  37. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/session.py +307 -62
  38. sshscript-4.0.2/src/sshscript/session.pyi +73 -0
  39. sshscript-4.0.2/src/sshscript/sessionsettings.py +93 -0
  40. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/sessionwrapper.py +141 -11
  41. sshscript-4.0.2/src/sshscript/sessionwrapper.pyi +24 -0
  42. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/sshscript.py +21 -12
  43. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/tokenparser.py +56 -1
  44. sshscript-4.0.2/src/sshscript/unittest/README.console-authentication.md +120 -0
  45. sshscript-4.0.2/src/sshscript/unittest/README.console-results.md +49 -0
  46. sshscript-4.0.2/src/sshscript/unittest/README.dollar-syntax-tests.md +57 -0
  47. sshscript-4.0.2/src/sshscript/unittest/README.module-tests.md +114 -0
  48. sshscript-3.1.5/src/sshscript/unittest/language.spy → sshscript-4.0.2/src/sshscript/unittest/language_fixture.spy +9 -112
  49. sshscript-4.0.2/src/sshscript/unittest/test_command_job.py +670 -0
  50. sshscript-4.0.2/src/sshscript/unittest/test_console_authentication.py +319 -0
  51. sshscript-4.0.2/src/sshscript/unittest/test_console_job.py +257 -0
  52. sshscript-4.0.2/src/sshscript/unittest/test_console_results.py +88 -0
  53. sshscript-4.0.2/src/sshscript/unittest/test_dollar_command_escapes.py +38 -0
  54. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_file_transfer.py +11 -3
  55. sshscript-4.0.2/src/sshscript/unittest/test_native_auth_runner.py +93 -0
  56. sshscript-4.0.2/src/sshscript/unittest/test_native_console_authentication.py +248 -0
  57. sshscript-4.0.2/src/sshscript/unittest/test_openssh_integration.py +497 -0
  58. sshscript-4.0.2/src/sshscript/unittest/test_public_types.py +29 -0
  59. sshscript-4.0.2/src/sshscript/unittest/test_python_version.py +113 -0
  60. sshscript-4.0.2/src/sshscript/unittest/test_recommended_examples.py +89 -0
  61. sshscript-4.0.2/src/sshscript/unittest/test_release_inputs.py +92 -0
  62. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_session_close_reporting.py +115 -0
  63. sshscript-4.0.2/src/sshscript/unittest/test_session_settings.py +154 -0
  64. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_ssh_config.py +36 -0
  65. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_ssh_security.py +18 -5
  66. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_sshscript_dollar_syntax.py +11 -0
  67. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_sshscript_module.py +20 -3
  68. sshscript-4.0.2/src/sshscript/unittest/test_stdin_contract.py +142 -0
  69. sshscript-4.0.2/src/sshscript/unittest/test_version_policy.py +48 -0
  70. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript.egg-info/PKG-INFO +214 -36
  71. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript.egg-info/SOURCES.txt +42 -2
  72. sshscript-4.0.2/src/sshscript.egg-info/entry_points.txt +2 -0
  73. sshscript-4.0.2/src/sshscript.egg-info/top_level.txt +2 -0
  74. sshscript-4.0.2/tools/check_baseline_examples.py +38 -0
  75. sshscript-4.0.2/tools/check_public_types.py +75 -0
  76. {sshscript-3.1.5 → sshscript-4.0.2}/tools/check_release.py +16 -1
  77. sshscript-4.0.2/tools/prepare_release.py +149 -0
  78. {sshscript-3.1.5 → sshscript-4.0.2}/tools/publish_release.py +4 -0
  79. sshscript-4.0.2/tools/run_checks.py +36 -0
  80. sshscript-4.0.2/tools/run_native_auth_ci.py +79 -0
  81. {sshscript-3.1.5 → sshscript-4.0.2}/tools/setup_openssh_ci.sh +7 -1
  82. sshscript-4.0.2/tools/sync_api_guide.py +38 -0
  83. sshscript-4.0.2/tools/version_policy.py +25 -0
  84. sshscript-3.1.5/CHANGELOG.md +0 -101
  85. sshscript-3.1.5/EXCEPTIONS.md +0 -54
  86. sshscript-3.1.5/MANIFEST.in +0 -7
  87. sshscript-3.1.5/RELEASING.md +0 -29
  88. sshscript-3.1.5/src/sshscript/channelutils.py +0 -444
  89. sshscript-3.1.5/src/sshscript/unittest/language_fixture.spy +0 -10
  90. sshscript-3.1.5/src/sshscript/unittest/test_openssh_integration.py +0 -311
  91. sshscript-3.1.5/src/sshscript.egg-info/entry_points.txt +0 -2
  92. sshscript-3.1.5/src/sshscript.egg-info/top_level.txt +0 -1
  93. sshscript-3.1.5/tools/prepare_release.py +0 -59
  94. sshscript-3.1.5/tools/run_checks.py +0 -19
  95. {sshscript-3.1.5 → sshscript-4.0.2}/.github/dependabot.yml +0 -0
  96. {sshscript-3.1.5 → sshscript-4.0.2}/.github/workflows/codeql.yml +0 -0
  97. {sshscript-3.1.5 → sshscript-4.0.2}/CODE_OF_CONDUCT.md +0 -0
  98. {sshscript-3.1.5 → sshscript-4.0.2}/LICENSE.txt +0 -0
  99. {sshscript-3.1.5 → sshscript-4.0.2}/SUPPORT.md +0 -0
  100. {sshscript-3.1.5 → sshscript-4.0.2}/setup.cfg +0 -0
  101. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/dollarparser.py +0 -0
  102. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/errorutils.py +0 -0
  103. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/spyimporter.py +0 -0
  104. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/sshconfig.py +0 -0
  105. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/stdio.py +0 -0
  106. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/check_package_asserts.py +0 -0
  107. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/dollar_syntax.spy +0 -0
  108. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/dollar_syntax_fixture.spy +0 -0
  109. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_channelgeneric_expect.py +0 -0
  110. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_check_file.py +0 -0
  111. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_command_api.py +0 -0
  112. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_logger_api.py +0 -0
  113. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_logger_integration.py +0 -0
  114. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_production_contract.py +0 -0
  115. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_session_proxy_cleanup.py +0 -0
  116. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_spy_source_mapping.py +0 -0
  117. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_spy_thread_session.py +0 -0
  118. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_stdio_dynamic_string.py +0 -0
  119. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_syntax_error.spy +0 -0
  120. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript/unittest/test_update_check.py +0 -0
  121. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript.egg-info/dependency_links.txt +0 -0
  122. {sshscript-3.1.5 → sshscript-4.0.2}/src/sshscript.egg-info/requires.txt +0 -0
@@ -0,0 +1,109 @@
1
+ name: Bug report
2
+ description: Report a reproducible problem in a supported SSHScript release
3
+ title: "[Bug] "
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thank you for helping improve SSHScript. Do not include credentials,
9
+ private host details, or production output. Report suspected
10
+ vulnerabilities through GitHub private vulnerability reporting instead.
11
+
12
+ - type: checkboxes
13
+ id: preflight
14
+ attributes:
15
+ label: Preflight
16
+ options:
17
+ - label: I reproduced this with the latest SSHScript 3.1.x release.
18
+ required: true
19
+ - label: I searched existing issues and discussions for this problem.
20
+ required: true
21
+ - label: This report contains no password, key, token, or private infrastructure detail.
22
+ required: true
23
+ - label: This is not a security vulnerability.
24
+ required: true
25
+
26
+ - type: input
27
+ id: sshscript-version
28
+ attributes:
29
+ label: SSHScript version
30
+ description: Paste the output of `sshscript --version`.
31
+ placeholder: "3.1.4"
32
+ validations:
33
+ required: true
34
+
35
+ - type: input
36
+ id: python-paramiko
37
+ attributes:
38
+ label: Python and Paramiko versions
39
+ placeholder: "Python 3.12.7; Paramiko 4.0.0"
40
+ validations:
41
+ required: true
42
+
43
+ - type: input
44
+ id: environment
45
+ attributes:
46
+ label: Operating system and SSH server
47
+ description: Include the SSH server implementation/version when relevant.
48
+ placeholder: "Ubuntu 24.04; OpenSSH_9.6p1"
49
+ validations:
50
+ required: true
51
+
52
+ - type: dropdown
53
+ id: interface
54
+ attributes:
55
+ label: Interface
56
+ options:
57
+ - Python module API
58
+ - Dollar syntax (.spy)
59
+ - Both
60
+ - Command-line interface
61
+ validations:
62
+ required: true
63
+
64
+ - type: dropdown
65
+ id: operation
66
+ attributes:
67
+ label: Operation
68
+ options:
69
+ - Local command
70
+ - Direct SSH command
71
+ - Nested SSH command
72
+ - Persistent or interactive console
73
+ - sudo or su
74
+ - Upload or download
75
+ - Import or source transformation
76
+ - Installation or packaging
77
+ - Other
78
+ validations:
79
+ required: true
80
+
81
+ - type: textarea
82
+ id: reproduction
83
+ attributes:
84
+ label: Minimal reproduction
85
+ description: Use a disposable host or credential-free example and include exact steps.
86
+ render: python
87
+ validations:
88
+ required: true
89
+
90
+ - type: textarea
91
+ id: expected
92
+ attributes:
93
+ label: Expected behavior
94
+ validations:
95
+ required: true
96
+
97
+ - type: textarea
98
+ id: actual
99
+ attributes:
100
+ label: Actual behavior
101
+ description: Include the exit status and a redacted traceback or error message.
102
+ validations:
103
+ required: true
104
+
105
+ - type: textarea
106
+ id: additional-context
107
+ attributes:
108
+ label: Additional context
109
+ description: Add sanitized details that are necessary to understand the issue.
@@ -0,0 +1,11 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Documentation
4
+ url: https://iapyeh.github.io/sshscript/v3a/
5
+ about: Read installation, tutorials, how-to guides, and reference material.
6
+ - name: Usage questions and design discussion
7
+ url: https://github.com/iapyeh/sshscript/discussions
8
+ about: Ask for help or discuss an idea before opening an issue.
9
+ - name: Report a security vulnerability privately
10
+ url: https://github.com/iapyeh/sshscript/security/advisories/new
11
+ about: Do not disclose a suspected vulnerability in a public issue.
@@ -0,0 +1,57 @@
1
+ name: Feature request
2
+ description: Propose a focused, testable improvement to SSHScript
3
+ title: "[Feature] "
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Explain the user problem before the proposed API. For usage questions
9
+ and early design discussion, use GitHub Discussions.
10
+
11
+ - type: checkboxes
12
+ id: preflight
13
+ attributes:
14
+ label: Preflight
15
+ options:
16
+ - label: I searched existing issues and discussions for this request.
17
+ required: true
18
+ - label: This request contains no credentials or private infrastructure detail.
19
+ required: true
20
+
21
+ - type: textarea
22
+ id: problem
23
+ attributes:
24
+ label: Problem or use case
25
+ description: What workflow is difficult today, and who is affected?
26
+ validations:
27
+ required: true
28
+
29
+ - type: textarea
30
+ id: proposal
31
+ attributes:
32
+ label: Proposed behavior
33
+ description: Show a small API or `.spy` example when that makes the behavior clearer.
34
+ validations:
35
+ required: true
36
+
37
+ - type: textarea
38
+ id: alternatives
39
+ attributes:
40
+ label: Alternatives considered
41
+ description: Describe existing SSHScript or Python approaches you tried.
42
+ validations:
43
+ required: true
44
+
45
+ - type: textarea
46
+ id: compatibility
47
+ attributes:
48
+ label: Compatibility and security impact
49
+ description: Note effects on the module API, dollar syntax, platforms, logging, credentials, or host-key handling.
50
+
51
+ - type: textarea
52
+ id: acceptance
53
+ attributes:
54
+ label: Acceptance criteria
55
+ description: List observable behavior that tests and documentation could verify.
56
+ validations:
57
+ required: true
@@ -0,0 +1,32 @@
1
+ ## Summary
2
+
3
+ <!-- What changes, and what user or maintainer problem does it solve? -->
4
+
5
+ ## Behavior and compatibility
6
+
7
+ <!-- Describe public API, dollar syntax, CLI, exception, security, or migration impact. -->
8
+
9
+ ## Validation
10
+
11
+ <!-- List exact commands and results. At minimum, run the credential-free gate. -->
12
+
13
+ - [ ] `python3 tools/run_checks.py`
14
+ - [ ] `python3 tools/check_release.py --output /tmp/sshscript-candidate-UNIQUE`
15
+ - [ ] A regression test covers the change, or the reason it cannot is explained.
16
+ - [ ] Relevant behavior was checked with and without `python -O`.
17
+
18
+ ## Documentation and release notes
19
+
20
+ - [ ] README or v3 documentation is updated for user-visible behavior.
21
+ - [ ] `CHANGELOG.md` is updated, or this change has no user-visible effect.
22
+ - [ ] Module API and dollar-syntax descriptions remain aligned.
23
+
24
+ ## Security and hygiene
25
+
26
+ - [ ] No credential, private key, token, internal hostname, private path, or production output is included.
27
+ - [ ] Host-key, command-injection, privilege, timeout, logging, and cleanup effects were considered.
28
+ - [ ] New dependencies or permissions are justified in the summary.
29
+
30
+ ## Related issue
31
+
32
+ <!-- Use "Closes #123" when appropriate. -->
@@ -19,9 +19,11 @@ jobs:
19
19
  with:
20
20
  python-version: ${{ matrix.python-version }}
21
21
  - name: Install test and build dependencies
22
- run: python -m pip install 'paramiko>=2.11,<5' 'packaging>=21' build twine
22
+ run: python -m pip install 'paramiko>=2.11,<5' 'packaging>=21' build twine 'mypy==2.3.1'
23
23
  - name: Test source
24
24
  run: python tools/run_checks.py
25
+ - name: Check public type contracts
26
+ run: python tools/check_public_types.py
25
27
  - name: Build and verify installed release
26
28
  run: python tools/check_release.py --output '${{ runner.temp }}/verified-artifacts'
27
29
  - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
@@ -29,14 +31,40 @@ jobs:
29
31
  name: sshscript-${{ matrix.os }}-${{ matrix.python-version }}
30
32
  path: ${{ runner.temp }}/verified-artifacts/
31
33
 
34
+ baseline-examples:
35
+ name: Published 3.1.5 examples
36
+ runs-on: ubuntu-latest
37
+ timeout-minutes: 5
38
+ steps:
39
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
40
+ with:
41
+ persist-credentials: false
42
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
43
+ with:
44
+ python-version: '3.11'
45
+ - run: python -m pip install 'sshscript==3.1.5'
46
+ - run: python -I tools/check_baseline_examples.py
47
+
32
48
  openssh-integration:
33
- name: OpenSSH integration (Python ${{ matrix.python-version }})
49
+ name: OpenSSH integration (Python ${{ matrix.python-version }}, ${{ matrix.dependency-label }})
34
50
  runs-on: ubuntu-latest
35
51
  timeout-minutes: 25
36
52
  strategy:
37
53
  fail-fast: false
38
54
  matrix:
39
- python-version: ['3.11', '3.14']
55
+ include:
56
+ - python-version: '3.11'
57
+ paramiko: 'paramiko==2.11.0'
58
+ dependency-label: minimum
59
+ - python-version: '3.14'
60
+ paramiko: 'paramiko==2.11.0'
61
+ dependency-label: minimum-new-python
62
+ - python-version: '3.11'
63
+ paramiko: 'paramiko>=3,<4'
64
+ dependency-label: 3.x
65
+ - python-version: '3.14'
66
+ paramiko: 'paramiko>=4,<5'
67
+ dependency-label: 4.x
40
68
  steps:
41
69
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
42
70
  with:
@@ -45,24 +73,37 @@ jobs:
45
73
  with:
46
74
  python-version: ${{ matrix.python-version }}
47
75
  - name: Install test and build dependencies
48
- run: python -m pip install 'paramiko>=2.11,<5' 'packaging>=21' build twine
76
+ run: python -m pip install "${{ matrix.paramiko }}" 'packaging>=21' build twine
77
+ - name: Test source with selected Paramiko
78
+ run: python tools/run_checks.py
49
79
  - name: Build and verify wheel
50
80
  run: python tools/check_release.py --output "$RUNNER_TEMP/verified-artifacts"
51
81
  - name: Install verified wheel
52
- run: python -m pip install --force-reinstall "$RUNNER_TEMP"/verified-artifacts/*.whl
82
+ run: python -m pip install --force-reinstall "$RUNNER_TEMP"/verified-artifacts/*.whl "${{ matrix.paramiko }}"
53
83
  - name: Provision loopback OpenSSH server
54
84
  run: bash tools/setup_openssh_ci.sh "$GITHUB_ENV"
55
85
  - name: Test installed wheel against OpenSSH
56
86
  run: |
57
87
  mkdir -p "$RUNNER_TEMP/openssh-tests"
58
- cp src/sshscript/unittest/test_openssh_integration.py "$RUNNER_TEMP/openssh-tests/"
88
+ TEST_SOURCE=unittest/test_openssh_integration.py
89
+ if [[ -d src/sshscript ]]; then TEST_SOURCE=src/sshscript/unittest/test_openssh_integration.py; fi
90
+ cp "$TEST_SOURCE" "$RUNNER_TEMP/openssh-tests/"
59
91
  cd "$RUNNER_TEMP/openssh-tests"
60
92
  python -I test_openssh_integration.py -v
93
+ - name: Verify native su/sudo contracts on local and SSH backends
94
+ run: python tools/run_native_auth_ci.py --wheel "$RUNNER_TEMP"/verified-artifacts/*.whl --report "$RUNNER_TEMP/native-auth-report.json"
95
+ - name: Save native authentication evidence
96
+ if: always()
97
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
98
+ with:
99
+ name: native-auth-python-${{ matrix.python-version }}-${{ matrix.dependency-label }}
100
+ if-no-files-found: warn
101
+ path: ${{ runner.temp }}/native-auth-report.json
61
102
  - name: Upload OpenSSH server log
62
103
  if: ${{ failure() && env.SSHSCRIPT_OPENSSH_LOG != '' }}
63
104
  uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
64
105
  with:
65
- name: openssh-log-python-${{ matrix.python-version }}
106
+ name: openssh-log-python-${{ matrix.python-version }}-${{ matrix.dependency-label }}
66
107
  if-no-files-found: warn
67
108
  path: ${{ env.SSHSCRIPT_OPENSSH_LOG }}
68
109
  - name: Remove disposable OpenSSH state
@@ -83,3 +124,21 @@ jobs:
83
124
  if [[ -n "${SSHSCRIPT_OPENSSH_ROOT:-}" && "$SSHSCRIPT_OPENSSH_ROOT" == "$RUNNER_TEMP"/sshscript-openssh.* ]]; then
84
125
  sudo rm -rf -- "$SSHSCRIPT_OPENSSH_ROOT"
85
126
  fi
127
+
128
+ # Preserve the required-check names configured on the protected release branch.
129
+ # Both gates require every supported Python/Paramiko integration leg to pass.
130
+ openssh-required-311:
131
+ name: OpenSSH integration (Python 3.11)
132
+ needs: openssh-integration
133
+ if: ${{ always() }}
134
+ runs-on: ubuntu-latest
135
+ steps:
136
+ - run: test "${{ needs.openssh-integration.result }}" = success
137
+
138
+ openssh-required-314:
139
+ name: OpenSSH integration (Python 3.14)
140
+ needs: openssh-integration
141
+ if: ${{ always() }}
142
+ runs-on: ubuntu-latest
143
+ steps:
144
+ - run: test "${{ needs.openssh-integration.result }}" = success
@@ -39,6 +39,8 @@ jobs:
39
39
  run: |
40
40
  PACKAGE_VERSION=$(python -c "import runpy; print(runpy.run_path('src/sshscript/_version.py')['__version__'])")
41
41
  test "$RELEASE_TAG" = "v$PACKAGE_VERSION"
42
+ - name: Reject development versions for production publication
43
+ run: python tools/version_policy.py --production
42
44
  - name: Stabilize build timestamps
43
45
  run: echo "SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)" >> "$GITHUB_ENV"
44
46
  - name: Run release gates and build verified artifacts
@@ -58,14 +60,26 @@ jobs:
58
60
  path: ${{ runner.temp }}/sshscript-release/
59
61
 
60
62
  openssh-integration:
61
- name: Release OpenSSH integration (Python ${{ matrix.python-version }})
63
+ name: Release OpenSSH integration (Python ${{ matrix.python-version }}, ${{ matrix.dependency-label }})
62
64
  needs: build
63
65
  runs-on: ubuntu-latest
64
66
  timeout-minutes: 15
65
67
  strategy:
66
68
  fail-fast: false
67
69
  matrix:
68
- python-version: ['3.11', '3.14']
70
+ include:
71
+ - python-version: '3.11'
72
+ paramiko: 'paramiko==2.11.0'
73
+ dependency-label: minimum
74
+ - python-version: '3.14'
75
+ paramiko: 'paramiko==2.11.0'
76
+ dependency-label: minimum-new-python
77
+ - python-version: '3.11'
78
+ paramiko: 'paramiko>=3,<4'
79
+ dependency-label: 3.x
80
+ - python-version: '3.14'
81
+ paramiko: 'paramiko>=4,<5'
82
+ dependency-label: 4.x
69
83
  steps:
70
84
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
71
85
  with:
@@ -78,20 +92,31 @@ jobs:
78
92
  name: python-package-distributions
79
93
  path: dist
80
94
  - name: Install verified wheel
81
- run: python -m pip install dist/*.whl
95
+ run: python -m pip install dist/*.whl "${{ matrix.paramiko }}"
82
96
  - name: Provision loopback OpenSSH server
83
97
  run: bash tools/setup_openssh_ci.sh "$GITHUB_ENV"
84
98
  - name: Test installed wheel against OpenSSH
85
99
  run: |
86
100
  mkdir -p "$RUNNER_TEMP/openssh-tests"
87
- cp src/sshscript/unittest/test_openssh_integration.py "$RUNNER_TEMP/openssh-tests/"
101
+ TEST_SOURCE=unittest/test_openssh_integration.py
102
+ if [[ -d src/sshscript ]]; then TEST_SOURCE=src/sshscript/unittest/test_openssh_integration.py; fi
103
+ cp "$TEST_SOURCE" "$RUNNER_TEMP/openssh-tests/"
88
104
  cd "$RUNNER_TEMP/openssh-tests"
89
105
  python -I test_openssh_integration.py -v
106
+ - name: Verify native su/sudo contracts on local and SSH backends
107
+ run: python tools/run_native_auth_ci.py --wheel dist/*.whl --report "$RUNNER_TEMP/native-auth-report.json"
108
+ - name: Save native authentication evidence
109
+ if: always()
110
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
111
+ with:
112
+ name: release-native-auth-python-${{ matrix.python-version }}-${{ matrix.dependency-label }}
113
+ if-no-files-found: warn
114
+ path: ${{ runner.temp }}/native-auth-report.json
90
115
  - name: Upload OpenSSH server log
91
116
  if: ${{ failure() && env.SSHSCRIPT_OPENSSH_LOG != '' }}
92
117
  uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
93
118
  with:
94
- name: release-openssh-log-python-${{ matrix.python-version }}
119
+ name: release-openssh-log-python-${{ matrix.python-version }}-${{ matrix.dependency-label }}
95
120
  if-no-files-found: warn
96
121
  path: ${{ env.SSHSCRIPT_OPENSSH_LOG }}
97
122
  - name: Remove disposable OpenSSH state
@@ -0,0 +1,121 @@
1
+ .DS_Store
2
+
3
+ # Python bytecode and caches
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+
11
+ # Environments
12
+ .venv/
13
+ venv/
14
+ env/
15
+
16
+ # Packaging output
17
+ build/
18
+ dist/
19
+ *.egg-info/
20
+ *.egg
21
+
22
+ # Coverage and local tooling
23
+ .coverage
24
+ .coverage.*
25
+ htmlcov/
26
+ .tox/
27
+ .nox/
28
+
29
+ # Editors
30
+ .idea/
31
+ .vscode/
32
+ *.swp
33
+ *~
34
+
35
+ # Local configuration and credentials
36
+ /unittest-v3/hostsall.py
37
+ .env
38
+ .env.*
39
+ *.pem
40
+ *.key
41
+ *secret*
42
+ *credential*
43
+ *password*
44
+
45
+ # Private host settings and environment-specific tests (kept locally)
46
+ /unittest-v3/A00commonc.py
47
+ /unittest-v3/B00.spy
48
+ /unittest-v3/B03nametest.py
49
+ /unittest-v3/apnic_test.spy
50
+ /unittest-v3/checkiterate.spy
51
+ /unittest-v3/checkproc.spy
52
+ /unittest-v3/checkpty.spy
53
+ /unittest-v3/checktest.spy
54
+ /unittest-v3/checktwodollars.spy
55
+ /unittest-v3/checkupdownload.spy
56
+ /unittest-v3/hosts4iapdev.py
57
+ /unittest-v3/hosts4macpro.py
58
+ /unittest-v3/hosts4rms4.py
59
+ /unittest-v3/hosts4test.py
60
+ /unittest-v3/log/A01onedollar.spy-1791280028.log
61
+ /unittest-v3/log/A01onedollar.spy-1791280318.log
62
+ /unittest-v3/log/A02twodollars.spy-1791280344.log
63
+ /unittest-v3/log/B04withsession.spy-1791280368.log
64
+ /unittest-v3/log/B04withsession.spy-1791288350.log
65
+ /unittest-v3/nonenterpython.spy
66
+ /unittest-v3/test.py
67
+ /unittest-v3/test.spy
68
+ /unittest-v3/usecase/iapdev.spy
69
+ /unittest-v3/usecase/settings.py
70
+ /unittest/README.language-tests.md
71
+ /unittest/a.spy
72
+ /unittest/a2.spy
73
+ /unittest/advance.spy
74
+ /unittest/basic.spy
75
+ /unittest/basic_dollar.spy
76
+ /unittest/language.spy
77
+
78
+ # Legacy tests are private; public tests require explicit approval.
79
+ /unittest-v3/
80
+ /unittest/*
81
+ !/unittest/README.console-authentication.md
82
+ !/unittest/README.console-results.md
83
+ !/unittest/README.dollar-syntax-tests.md
84
+ !/unittest/README.module-tests.md
85
+ !/unittest/check_package_asserts.py
86
+ !/unittest/dollar_syntax.spy
87
+ !/unittest/dollar_syntax_fixture.spy
88
+ !/unittest/language_fixture.spy
89
+ !/unittest/test_channelgeneric_expect.py
90
+ !/unittest/test_check_file.py
91
+ !/unittest/test_command_api.py
92
+ !/unittest/test_command_job.py
93
+ !/unittest/test_console_authentication.py
94
+ !/unittest/test_native_console_authentication.py
95
+ !/unittest/test_native_auth_runner.py
96
+ !/unittest/test_console_results.py
97
+ !/unittest/test_console_job.py
98
+ !/unittest/test_dollar_command_escapes.py
99
+ !/unittest/test_file_transfer.py
100
+ !/unittest/test_logger_api.py
101
+ !/unittest/test_logger_integration.py
102
+ !/unittest/test_openssh_integration.py
103
+ !/unittest/test_production_contract.py
104
+ !/unittest/test_public_types.py
105
+ !/unittest/test_python_version.py
106
+ !/unittest/test_version_policy.py
107
+ !/unittest/test_release_inputs.py
108
+ !/unittest/test_recommended_examples.py
109
+ !/unittest/test_session_close_reporting.py
110
+ !/unittest/test_session_proxy_cleanup.py
111
+ !/unittest/test_session_settings.py
112
+ !/unittest/test_spy_source_mapping.py
113
+ !/unittest/test_spy_thread_session.py
114
+ !/unittest/test_ssh_config.py
115
+ !/unittest/test_ssh_security.py
116
+ !/unittest/test_sshscript_dollar_syntax.py
117
+ !/unittest/test_sshscript_module.py
118
+ !/unittest/test_stdio_dynamic_string.py
119
+ !/unittest/test_stdin_contract.py
120
+ !/unittest/test_syntax_error.spy
121
+ !/unittest/test_update_check.py