python-rundeck 0.1.0__tar.gz → 0.3.0__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 (28) hide show
  1. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/PKG-INFO +135 -127
  2. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/README.md +129 -126
  3. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/pyproject.toml +8 -1
  4. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/LICENSE +0 -0
  5. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/__init__.py +0 -0
  6. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/base.py +0 -0
  7. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/client.py +0 -0
  8. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/config.py +0 -0
  9. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/const.py +0 -0
  10. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/exceptions.py +0 -0
  11. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/py.typed +0 -0
  12. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/__init__.py +0 -0
  13. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/__init__.py +0 -0
  14. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/adhoc.py +0 -0
  15. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/config_management.py +0 -0
  16. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/executions.py +0 -0
  17. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/features.py +0 -0
  18. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/jobs.py +0 -0
  19. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/key_storage.py +0 -0
  20. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/metrics.py +0 -0
  21. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/plugins.py +0 -0
  22. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/projects.py +0 -0
  23. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/scheduler.py +0 -0
  24. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/scm.py +0 -0
  25. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/system.py +0 -0
  26. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/tokens.py +0 -0
  27. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/users.py +0 -0
  28. {python_rundeck-0.1.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/webhooks.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-rundeck
3
- Version: 0.1.0
3
+ Version: 0.3.0
4
4
  Summary: Python client for the Rundeck API (v14-v56).
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -29,48 +29,55 @@ Requires-Dist: pytest-cov (>=5.0.0,<6.0.0) ; extra == "dev"
29
29
  Requires-Dist: requests (>=2.32.5,<3.0.0)
30
30
  Requires-Dist: ruff (>=0.7.0,<1.0.0) ; extra == "dev"
31
31
  Requires-Dist: safety (>=3.2.0,<4.0.0) ; extra == "dev"
32
+ Project-URL: Changelog, https://gitlab.com/pascal.seckinger/python-rundeck/-/blob/main/CHANGELOG.md
33
+ Project-URL: Documentation, https://gitlab.com/pascal.seckinger/python-rundeck/-/blob/main/README.md
34
+ Project-URL: Homepage, https://gitlab.com/pascal.seckinger/python-rundeck
35
+ Project-URL: Issues, https://gitlab.com/pascal.seckinger/python-rundeck/-/issues
36
+ Project-URL: Repository, https://gitlab.com/pascal.seckinger/python-rundeck
32
37
  Description-Content-Type: text/markdown
33
38
 
34
39
  python-rundeck
35
40
  ===============
41
+ ![PyPI Version](https://img.shields.io/pypi/v/python-rundeck)
42
+ ![Python Versions](https://img.shields.io/pypi/pyversions/python-rundeck)
43
+ ![License](https://img.shields.io/pypi/l/python-rundeck)
44
+ ![Downloads](https://static.pepy.tech/badge/python-rundeck)
36
45
 
37
- Client Python pour l’API Rundeck (v14v56), inspiré de l’architecture de python-gitlab. Fournit des managers typés pour les ressources clés (projects, jobs, executions, tokens, users, système, configuration) et SCM (import/export).
46
+ Python client for the Rundeck API (v14-v56), inspired by the python-gitlab architecture. Provides typed managers for key resources (projects, jobs, executions, tokens, users, system, configuration) and SCM (import/export).
38
47
 
39
- Sommaire
48
+ Contents
40
49
  --------
41
50
  - Installation
42
- - Démarrage rapide
51
+ - Quick start
43
52
  - Configuration
44
- - Ressources disponibles
45
- - Exemples par ressource
46
- - Gestion des erreurs
47
- - Développement et tests
53
+ - Available resources
54
+ - Examples by resource
55
+ - Error handling
56
+ - Development and testing
48
57
 
49
58
  Installation
50
59
  ------------
51
- Prerequis : Python 3.11+. Le projet utilise Poetry.
60
+ Prerequisites: Python 3.11+. Install from PyPI.
52
61
 
53
62
  ```bash
54
- poetry install
55
- # ou en editable avec pip
56
- pip install -e .
63
+ pip install python-rundeck
57
64
  ```
58
65
 
59
- Démarrage rapide
60
- ----------------
66
+ Quick start
67
+ -----------
61
68
  ```python
62
69
  from rundeck.client import Rundeck
63
70
 
64
71
  rd = Rundeck(url="https://rundeck.example.com", token="MY_TOKEN", api_version=56)
65
- # Auth par mot de passe (session cookie) si pas de token
72
+ # Password auth (session cookie) if no token
66
73
  rd = Rundeck(url="https://rundeck.example.com", username="admin", password="admin", api_version=56)
67
74
 
68
- # Lister les projets
75
+ # List projects
69
76
  projects = rd.projects.list()
70
77
  for p in projects:
71
78
  print(p.name)
72
79
 
73
- # Récupérer un projet et lancer un job
80
+ # Get a project and run a job
74
81
  project = rd.projects.get("demo")
75
82
  jobs = project.jobs.list()
76
83
  execu = jobs[0].run()
@@ -79,64 +86,64 @@ print(execu.id)
79
86
 
80
87
  Configuration
81
88
  -------------
82
- La configuration suit un modèle en cascade (args > env > fichiers > défauts) via `RundeckConfig`.
83
-
84
- Paramètres principaux :
85
- - `url` : URL Rundeck (ex. `https://rundeck.example.com`)
86
- - `token` : Token API (en-tête `X-Rundeck-Auth-Token`)
87
- - `username` / `password` : Authentification par session (j_security_check) si aucun token n'est fourni.
88
- - `api_version` : Version d’API (ex. `56`)
89
- - `timeout` : Timeout des requêtes (float, secondes)
90
- - `ssl_verify` : Vérification TLS (bool ou chemin CA)
91
-
92
- Fichiers de config : si besoin, passez `config_files` ou `Rundeck.from_config(config_section=...)`.
93
- Variables d'env utiles (config client) :
94
- - `RUNDECK_URL` : URL de base
95
- - `RUNDECK_TOKEN` : token API (en-tête `X-Rundeck-Auth-Token`)
96
- - `RUNDECK_USERNAME` / `RUNDECK_PASSWORD` : auth session (si pas de token)
97
- - `RUNDECK_API_VERSION` : version d'API (ex: 56)
98
- - `RUNDECK_TIMEOUT` : timeout des requêtes (secondes)
99
- - `RUNDECK_SSL_VERIFY` : vérification TLS (bool ou chemin CA)
100
- - `RUNDECK_USER_AGENT` : User-Agent HTTP
101
-
102
- Ressources disponibles
103
- ----------------------
104
- - `projects` (`ProjectManager`) : CRUD projets, export/import de jobs, config projet, archive (export/import).
105
- - `jobs` (`JobManager`) : liste, get, suppressions, actions bulk, exécution.
106
- - `executions` (`ExecutionManager`) : liste/filtre, running, get/delete, query avancée.
107
- - `tokens` (`TokenManager`) : liste, get, create, delete.
108
- - `users` (`UserManager`) : opérations utilisateurs (selon implémentation courante).
109
- - `metrics` (`MetricsManager`) : endpoints `/metrics` (list/data/healthcheck/ping).
110
- - `plugins` (`PluginManager`) : liste des plugins installés (`/plugin/list`).
111
- - `webhooks` (`WebhookEventManager` + `ProjectWebhookManager`) : gestion des webhooks projet et envoi via token.
112
- - `key_storage` (`StorageKeyManager`) : gestion du stockage des clés `/storage/keys`.
113
- - `adhoc` (`AdhocManager`, via `project.adhoc`) : exécution de commandes/scripts AdHoc.
114
- - `system` (`SystemManager`) : info système, exécutions enable/disable, logstorage, ACL.
115
- - `config_management` (`ConfigManagementManager`) : configuration globale `/config`.
116
- - `scm` (via `project.scm` et `job.scm`) : plugins import/export, setup, enable/disable, statut, actions (commit/import/export...).
117
-
118
- Exemples par ressource (complets)
119
- ---------------------------------
120
- Projets
89
+ Configuration follows a cascading model (args > env > files > defaults) via `RundeckConfig`.
90
+
91
+ Main parameters:
92
+ - `url`: Rundeck URL (e.g. `https://rundeck.example.com`)
93
+ - `token`: API token (header `X-Rundeck-Auth-Token`)
94
+ - `username` / `password`: Session authentication (j_security_check) if no token is provided.
95
+ - `api_version`: API version (e.g. `56`)
96
+ - `timeout`: Request timeout (float, seconds)
97
+ - `ssl_verify`: TLS verification (bool or CA path)
98
+
99
+ Config files: if needed, pass `config_files` or `Rundeck.from_config(config_section=...)`.
100
+ Useful env vars (client config):
101
+ - `RUNDECK_URL`: Base URL
102
+ - `RUNDECK_TOKEN`: API token (header `X-Rundeck-Auth-Token`)
103
+ - `RUNDECK_USERNAME` / `RUNDECK_PASSWORD`: Session auth (if no token)
104
+ - `RUNDECK_API_VERSION`: API version (e.g. 56)
105
+ - `RUNDECK_TIMEOUT`: Request timeout (seconds)
106
+ - `RUNDECK_SSL_VERIFY`: TLS verification (bool or CA path)
107
+ - `RUNDECK_USER_AGENT`: HTTP User-Agent
108
+
109
+ Available resources
110
+ -------------------
111
+ - `projects` (`ProjectManager`): CRUD projects, job export/import, project config, archive (export/import).
112
+ - `jobs` (`JobManager`): list, get, deletions, bulk actions, execution.
113
+ - `executions` (`ExecutionManager`): list/filter, running, get/delete, advanced query.
114
+ - `tokens` (`TokenManager`): list, get, create, delete.
115
+ - `users` (`UserManager`): user operations (per current implementation).
116
+ - `metrics` (`MetricsManager`): `/metrics` endpoints (list/data/healthcheck/ping).
117
+ - `plugins` (`PluginManager`): list installed plugins (`/plugin/list`).
118
+ - `webhooks` (`WebhookEventManager` + `ProjectWebhookManager`): project webhook management and sending via token.
119
+ - `key_storage` (`StorageKeyManager`): key storage management `/storage/keys`.
120
+ - `adhoc` (`AdhocManager`, via `project.adhoc`): run AdHoc commands/scripts.
121
+ - `system` (`SystemManager`): system info, executions enable/disable, logstorage, ACL.
122
+ - `config_management` (`ConfigManagementManager`): global configuration `/config`.
123
+ - `scm` (via `project.scm` and `job.scm`): import/export plugins, setup, enable/disable, status, actions (commit/import/export...).
124
+
125
+ Examples by resource (complete)
126
+ -------------------------------
127
+ Projects
121
128
  ```python
122
- # CRUD projet
129
+ # CRUD project
123
130
  p = rd.projects.create("demo")
124
131
  p = rd.projects.get("demo")
125
132
  rd.projects.delete("demo")
126
133
  projects = rd.projects.list()
127
134
 
128
- # Export / import de jobs d'un projet (via le manager de jobs)
135
+ # Export / import jobs from a project (via job manager)
129
136
  project.jobs.export(format="json", idlist="id1,id2", groupPath="group/sub")
130
137
  project.jobs.import_jobs(content=open("jobs.json").read(), format="json", dupeOption="update", uuidOption="remove")
131
138
 
132
- # Export / import du projet (archive ZIP)
139
+ # Project export / import (ZIP archive)
133
140
  archive = project.archive
134
- resp = archive.export(export_all=False, export_webhooks=True) # Response brute (zip)
141
+ resp = archive.export(export_all=False, export_webhooks=True) # Raw response (zip)
135
142
  token_info = archive.export_async(exportAll=True)
136
143
  status = archive.export_status(token_info.get("token", ""))
137
144
  zip_resp = archive.export_download(token_info.get("token", ""))
138
145
 
139
- # Import d'archive (synchrone ou async)
146
+ # Archive import (sync or async)
140
147
  archive.import_archive(
141
148
  content=open("project-export.zip", "rb").read(),
142
149
  jobUuidOption="preserve",
@@ -146,16 +153,16 @@ archive.import_archive(
146
153
  archive.import_archive(content=open("project-export.zip", "rb").read(), async_import=True)
147
154
  archive.import_status()
148
155
 
149
- # Readme / MOTD du projet
150
- project.readme.get_readme() # texte par défaut
156
+ # Project README / MOTD
157
+ project.readme.get_readme() # default text
151
158
  project.readme.get_readme(accept="application/json")
152
- project.readme.update_readme("Nouveau contenu", content_type="text/plain")
159
+ project.readme.update_readme("New content", content_type="text/plain")
153
160
  project.readme.delete_readme()
154
161
  project.readme.get_motd()
155
- project.readme.update_motd("Message du jour", content_type="text/plain")
162
+ project.readme.update_motd("Message of the day", content_type="text/plain")
156
163
  project.readme.delete_motd()
157
164
 
158
- # Config projet (clé/valeur)
165
+ # Project config (key/value)
159
166
  conf = p.config.get()
160
167
  p.config.keys.get("project.label")
161
168
  p.config.keys.set("project.label", "Demo")
@@ -163,27 +170,27 @@ p.config.keys.update({"project.description": "Sample"})
163
170
  p.config.replace({"project.label": "Demo", "project.description": "Sample"})
164
171
  p.config.keys.delete("project.label")
165
172
 
166
- # SCM import/export (sur un projet)
167
- scm_import = project.scm.import_ # ou getattr(project.scm, "import")
173
+ # SCM import/export (on a project)
174
+ scm_import = project.scm.import_ # or getattr(project.scm, "import")
168
175
  scm_export = project.scm.export
169
176
 
170
- # Découverte des plugins
177
+ # Plugin discovery
171
178
  scm_import.plugins.list()
172
179
  scm_export.plugins.list()
173
180
 
174
- # Champs d'entrée pour un plugin et setup (plugin_type explicite)
181
+ # Input fields for a plugin and setup (explicit plugin_type)
175
182
  fields = scm_import.plugins.input_fields("git-import")
176
183
  scm_import.config.setup("git-import", {"url": "ssh://git@example.com/repo.git", "dir": "/tmp/repo"})
177
184
 
178
- # Activer/désactiver un plugin
185
+ # Enable/disable a plugin
179
186
  scm_import.config.enable("git-import")
180
187
  scm_export.config.disable("custom-export")
181
188
 
182
- # Statut/config SCM
189
+ # SCM status/config
183
190
  import_status = scm_import.actions.status()
184
191
  export_conf = scm_export.config.get()
185
192
 
186
- # Actions SCM côté projet (ex: commit/pull/push selon plugin)
193
+ # Project-side SCM actions (e.g., commit/pull/push depending on plugin)
187
194
  action_fields = scm_export.actions.input_fields("commit")
188
195
  scm_export.actions.perform(
189
196
  "commit",
@@ -196,14 +203,14 @@ scm_export.actions.perform(
196
203
 
197
204
  Jobs
198
205
  ```python
199
- # Lister les jobs d’un projet
206
+ # List jobs for a project
200
207
  jobs = rd.jobs.list(project="demo", groupPath="ops")
201
208
 
202
- # Depuis un projet parenté
209
+ # From a parent project
203
210
  project = rd.projects.get("demo")
204
211
  jobs = project.jobs.list()
205
212
 
206
- # Accès direct à un job
213
+ # Direct access to a job
207
214
  job = rd.jobs.get("job-id")
208
215
  job.delete()
209
216
  job.definition(format="yaml")
@@ -218,7 +225,7 @@ tags = job.tags()
218
225
  workflow = job.workflow()
219
226
  forecast = job.forecast(time="2024-05-01T10:00:00Z", max=5)
220
227
 
221
- # Exporter/importer des jobs via le manager (paramètre project ou parent)
228
+ # Export/import jobs via manager (project parameter or parent)
222
229
  rd.jobs.export(project="demo", format="xml", idlist="id1,id2", groupPath="group")
223
230
  rd.jobs.import_jobs(
224
231
  project="demo",
@@ -226,30 +233,30 @@ rd.jobs.import_jobs(
226
233
  fileformat="xml",
227
234
  dupeOption="update",
228
235
  )
229
- # Ou via un projet parenté
236
+ # Or via a parent project
230
237
  project.jobs.export(format="json")
231
238
  project.jobs.import_jobs(content=open("jobs.json", "rb").read(), fileformat="json")
232
239
 
233
- Note: l'import reste exposé sous le nom `import_jobs(...)` (le mot-clé Python empêche un appel direct à `.import`). Si vous préférez l'alias, utilisez `getattr(rd.jobs, "import")(...)`.
240
+ Note: import remains exposed as `import_jobs(...)` (the Python keyword prevents a direct call to `.import`). If you prefer the alias, use `getattr(rd.jobs, "import")(...)`.
234
241
 
235
- # Exécuter et récupérer l'exécution
242
+ # Run and get the execution
236
243
  execution = job.run(argString="-option value")
237
244
 
238
- # Actions bulk
245
+ # Bulk actions
239
246
  rd.jobs.bulk.enable_execution(["id1", "id2"])
240
247
  rd.jobs.bulk.disable_execution(["id1", "id2"])
241
248
  rd.jobs.bulk.delete(["id1", "id2"])
242
249
  rd.jobs.bulk.enable_schedule(["id1", "id2"])
243
250
  rd.jobs.bulk.disable_schedule(["id1", "id2"])
244
251
 
245
- # Upload de fichiers d'option et fichiers uploadés
252
+ # Upload option files and uploaded files
246
253
  job.upload_option_file("csvfile", open("data.csv", "rb").read(), file_name="data.csv")
247
254
  job.list_uploaded_files(max=20)
248
255
  rd.jobs.get_uploaded_file_info("file-id")
249
256
 
250
- # SCM import/export sur un job
257
+ # SCM import/export on a job
251
258
  job_scm_export = job.scm.export
252
- job_scm_import = job.scm.import_ # ou getattr(job.scm, "import")
259
+ job_scm_import = job.scm.import_ # or getattr(job.scm, "import")
253
260
 
254
261
  job_scm_export.status()
255
262
  job_scm_export.diff()
@@ -259,7 +266,7 @@ job_scm_import.status()
259
266
  job_scm_import.input_fields("pull")
260
267
  job_scm_import.perform("pull", input_values={"message": "Update from repo"})
261
268
 
262
- # Ressources d'un projet
269
+ # Project resources
263
270
  resources = project.resources.list(format="json", groupPath="ops")
264
271
  node = project.resources.get("node1")
265
272
  sources = project.sources.list()
@@ -273,18 +280,18 @@ project.acl.update("policy.aclpolicy", content="...yaml...")
273
280
  project.acl.delete("policy.aclpolicy")
274
281
  ```
275
282
 
276
- Exécutions
283
+ Executions
277
284
  ```python
278
- # Liste simple ou paginée
285
+ # Simple or paginated list
279
286
  execs = rd.executions.list(project="demo", status="running", max=50, offset=0)
280
- running = rd.executions.running(project="demo") # ou "*" pour tous
287
+ running = rd.executions.running(project="demo") # or "*" for all
281
288
 
282
- # Détails / suppression
289
+ # Details / deletion
283
290
  e = rd.executions.get("123")
284
291
  rd.executions.delete("123")
285
292
  rd.executions.bulk_delete(["123", "124"])
286
293
 
287
- # Requête avancée (query)
294
+ # Advanced query
288
295
  advanced = rd.executions.query(
289
296
  project="demo",
290
297
  statusFilter="failed",
@@ -294,12 +301,12 @@ advanced = rd.executions.query(
294
301
  max=100,
295
302
  )
296
303
 
297
- # Méthodes sur Execution
304
+ # Execution methods
298
305
  e.abort(asUser="admin")
299
306
  output = e.get_output(offset=0, maxlines=100)
300
307
  state = e.get_state()
301
308
  is_running = e.is_running()
302
- e.refresh() # recharge les données
309
+ e.refresh() # reload data
303
310
  ```
304
311
 
305
312
  Tokens
@@ -311,20 +318,20 @@ new_token = rd.tokens.create(user="alice", roles=["admin"], duration="90d", name
311
318
  rd.tokens.delete(new_token.id)
312
319
  ```
313
320
 
314
- Utilisateurs
321
+ Users
315
322
  ```python
316
323
  users = rd.users.list()
317
324
  me = rd.users.get_current()
318
325
  u = rd.users.get("bob")
319
326
  roles = rd.users.current_roles()
320
327
 
321
- # Mise à jour via manager ou objet
328
+ # Update via manager or object
322
329
  u = rd.users.update("bob", firstName="Bob", lastName="Builder", email="bob@example.com")
323
- u.roles() # via l'objet
324
- u.update(email="new@example.com") # met à jour et rafraîchit l'objet
330
+ u.roles() # via the object
331
+ u.update(email="new@example.com") # updates and refreshes the object
325
332
  ```
326
333
 
327
- Système
334
+ System
328
335
  ```python
329
336
  system = rd.system
330
337
  info = system.info()
@@ -334,7 +341,7 @@ system.logstorage.info()
334
341
  system.logstorage.incomplete(max=50, offset=0)
335
342
  system.logstorage.incomplete_resume()
336
343
 
337
- # Mode exécution (sous-manager)
344
+ # Execution mode (sub-manager)
338
345
  system.executions.enable()
339
346
  system.executions.disable()
340
347
  system.executions.status()
@@ -351,7 +358,7 @@ rd.scheduler.takeover(all_servers=True)
351
358
  rd.scheduler.takeover(server_uuid="uuid-123", project="demo", job_id="job-1")
352
359
  ```
353
360
 
354
- Métriques
361
+ Metrics
355
362
  ```python
356
363
  metrics = rd.metrics
357
364
  metrics.list()
@@ -359,16 +366,16 @@ metrics.data()
359
366
  metrics.healthcheck()
360
367
  metrics.ping()
361
368
 
362
- # Plugins installés
369
+ # Installed plugins
363
370
  plugins = rd.plugins.list()
364
371
  for plugin in plugins:
365
372
  print(plugin.name, plugin.service)
366
373
 
367
- # Détail d'un plugin
374
+ # Plugin details
368
375
  first = plugins[0]
369
376
  detail = rd.plugins.detail(first.service, first.name)
370
377
 
371
- # Webhooks projet et envoi
378
+ # Project webhooks and send
372
379
  project = rd.projects.get("demo")
373
380
  wh = project.webhooks
374
381
  wh.create(
@@ -388,9 +395,9 @@ wh.delete(first_hook.id)
388
395
 
389
396
  # Key storage (/storage/keys)
390
397
  ks = rd.key_storage
391
- # Créer un secret (password)
398
+ # Create a secret (password)
392
399
  ks.create("integration/secret1", content="s3cr3t", content_type="application/x-rundeck-data-password")
393
- resources = ks.list() # liste racine
400
+ resources = ks.list() # root listing
394
401
  meta = ks.get_metadata("integration/secret1")
395
402
  ks.delete("integration/secret1")
396
403
 
@@ -398,17 +405,17 @@ ks.delete("integration/secret1")
398
405
  project = rd.projects.get("demo")
399
406
  exec1 = project.adhoc.run_command("echo 'hello world'")
400
407
  exec2 = project.adhoc.run_script("echo 'from script'")
401
- # exec2.id pour suivre l'exécution
402
- # Mode stub (sans refresh immédiat) puis refresh manuel
408
+ # exec2.id to track execution
409
+ # Stub mode (no immediate refresh) then manual refresh
403
410
  exec_stub = project.adhoc.run_command("echo stub", refresh=False)
404
- exec_stub.refresh() # charge l'objet complet
411
+ exec_stub.refresh() # load the full object
405
412
  # Script via multipart (upload)
406
413
  exec3 = project.adhoc.run_script(
407
414
  script_file=("hello.sh", "echo multipart", "text/plain"),
408
415
  refresh=False,
409
416
  )
410
417
 
411
- # Features système
418
+ # System features
412
419
  features = rd.features.list()
413
420
  if features:
414
421
  first = features[0]
@@ -416,7 +423,7 @@ if features:
416
423
  print(first.name, status.enabled)
417
424
  ```
418
425
 
419
- Configuration globale `/config`
426
+ Global configuration `/config`
420
427
  ```python
421
428
  cfg = rd.config_management
422
429
  all_configs = cfg.list()
@@ -428,27 +435,28 @@ cfg.restart()
428
435
 
429
436
  Pagination
430
437
  ----------
431
- Les managers héritent de `RundeckObjectManager.iter(...)` (offset/max). Exemple :
438
+ Managers inherit `RundeckObjectManager.iter(...)` (offset/max). Example:
432
439
  ```python
433
440
  for job in rd.jobs.iter(project="demo", page_size=100):
434
441
  print(job.id)
435
442
  ```
436
443
 
437
- Gestion des erreurs
438
- -------------------
439
- Les erreurs HTTP passent par `raise_for_status` et lèvent des exceptions dédiées (ex. `RundeckAuthenticationError`, `RundeckNotFoundError`, `RundeckValidationError`, `RundeckConflictError`, `RundeckServerError`). Gérez-les avec un bloc `try/except` autour des appels client.
440
-
441
- Développement et tests
442
- ----------------------
443
- - Formatage/Lint : `black src/rundeck tests/`, `ruff check src/rundeck tests/`
444
- - Typage : `mypy src/rundeck/`
445
- - Tests : `pytest`
446
- - Tests d'intégration locaux : `scripts/run-integration.sh` (démarre docker compose à la racine, exporte par défaut `RUNDECK_URL=http://localhost:4440`, `RUNDECK_TOKEN=adminToken`, `RUNDECK_API_VERSION=56`, attend le healthcheck, lance `poetry run pytest -m integration`). Vous pouvez surcharger les variables d'environnement avant l'appel. Ajoutez `KEEP_STACK=1` pour ne pas arrêter l'instance après les tests. Script de teardown dédié : `scripts/stop-integration.sh`.
447
-
448
- Structure du code
449
- -----------------
450
- - `src/rundeck/base.py` : objets/manager génériques, helpers `_build_path`, pagination, CRUD.
451
- - `src/rundeck/client.py` : client HTTP, méthodes `http_get/post/put/delete/list`.
452
- - `src/rundeck/v1/objects/` : managers et objets métiers (projects, jobs, executions, system, tokens, users, config_management).
453
- - Schéma managers/objets : on applique le pattern manager → objet partout où l’API expose une ressource identifiable (jobs, projects, users, tokens, executions, etc.). Pour les endpoints purement globaux ou utilitaires sans ressource (ex. `/system` et ses sous-domaines, `/config`, `/metrics`), on utilise des sous-managers dédiés plutôt que de forcer un objet artificiel.
444
+ Error handling
445
+ --------------
446
+ HTTP errors go through `raise_for_status` and raise dedicated exceptions (e.g., `RundeckAuthenticationError`, `RundeckNotFoundError`, `RundeckValidationError`, `RundeckConflictError`, `RundeckServerError`). Handle them with a `try/except` block around client calls.
447
+
448
+ Development and testing
449
+ -----------------------
450
+ - Development setup (contributors): clone the repo and run `poetry install`.
451
+ - Formatting/Lint: `black src/rundeck tests/`, `ruff check src/rundeck tests/`
452
+ - Typing: `mypy src/rundeck/`
453
+ - Tests: `pytest`
454
+ - Local integration tests: `scripts/run-integration.sh` (starts docker compose at repo root, exports by default `RUNDECK_URL=http://localhost:4440`, `RUNDECK_TOKEN=adminToken`, `RUNDECK_API_VERSION=56`, waits for the healthcheck, runs `poetry run pytest -m integration`). You can override env vars before running. Add `KEEP_STACK=1` to keep the instance running after tests. Dedicated teardown script: `scripts/stop-integration.sh`.
455
+
456
+ Code structure
457
+ --------------
458
+ - `src/rundeck/base.py`: generic objects/managers, `_build_path` helpers, pagination, CRUD.
459
+ - `src/rundeck/client.py`: HTTP client, `http_get/post/put/delete/list` methods.
460
+ - `src/rundeck/v1/objects/`: domain managers and objects (projects, jobs, executions, system, tokens, users, config_management).
461
+ - Managers/objects schema: we apply the manager -> object pattern wherever the API exposes an identifiable resource (jobs, projects, users, tokens, executions, etc.). For purely global or utility endpoints without a resource (e.g., `/system` and its subdomains, `/config`, `/metrics`), we use dedicated sub-managers rather than forcing an artificial object.
454
462
 
@@ -1,43 +1,45 @@
1
1
  python-rundeck
2
2
  ===============
3
+ ![PyPI Version](https://img.shields.io/pypi/v/python-rundeck)
4
+ ![Python Versions](https://img.shields.io/pypi/pyversions/python-rundeck)
5
+ ![License](https://img.shields.io/pypi/l/python-rundeck)
6
+ ![Downloads](https://static.pepy.tech/badge/python-rundeck)
3
7
 
4
- Client Python pour l’API Rundeck (v14v56), inspiré de l’architecture de python-gitlab. Fournit des managers typés pour les ressources clés (projects, jobs, executions, tokens, users, système, configuration) et SCM (import/export).
8
+ Python client for the Rundeck API (v14-v56), inspired by the python-gitlab architecture. Provides typed managers for key resources (projects, jobs, executions, tokens, users, system, configuration) and SCM (import/export).
5
9
 
6
- Sommaire
10
+ Contents
7
11
  --------
8
12
  - Installation
9
- - Démarrage rapide
13
+ - Quick start
10
14
  - Configuration
11
- - Ressources disponibles
12
- - Exemples par ressource
13
- - Gestion des erreurs
14
- - Développement et tests
15
+ - Available resources
16
+ - Examples by resource
17
+ - Error handling
18
+ - Development and testing
15
19
 
16
20
  Installation
17
21
  ------------
18
- Prerequis : Python 3.11+. Le projet utilise Poetry.
22
+ Prerequisites: Python 3.11+. Install from PyPI.
19
23
 
20
24
  ```bash
21
- poetry install
22
- # ou en editable avec pip
23
- pip install -e .
25
+ pip install python-rundeck
24
26
  ```
25
27
 
26
- Démarrage rapide
27
- ----------------
28
+ Quick start
29
+ -----------
28
30
  ```python
29
31
  from rundeck.client import Rundeck
30
32
 
31
33
  rd = Rundeck(url="https://rundeck.example.com", token="MY_TOKEN", api_version=56)
32
- # Auth par mot de passe (session cookie) si pas de token
34
+ # Password auth (session cookie) if no token
33
35
  rd = Rundeck(url="https://rundeck.example.com", username="admin", password="admin", api_version=56)
34
36
 
35
- # Lister les projets
37
+ # List projects
36
38
  projects = rd.projects.list()
37
39
  for p in projects:
38
40
  print(p.name)
39
41
 
40
- # Récupérer un projet et lancer un job
42
+ # Get a project and run a job
41
43
  project = rd.projects.get("demo")
42
44
  jobs = project.jobs.list()
43
45
  execu = jobs[0].run()
@@ -46,64 +48,64 @@ print(execu.id)
46
48
 
47
49
  Configuration
48
50
  -------------
49
- La configuration suit un modèle en cascade (args > env > fichiers > défauts) via `RundeckConfig`.
50
-
51
- Paramètres principaux :
52
- - `url` : URL Rundeck (ex. `https://rundeck.example.com`)
53
- - `token` : Token API (en-tête `X-Rundeck-Auth-Token`)
54
- - `username` / `password` : Authentification par session (j_security_check) si aucun token n'est fourni.
55
- - `api_version` : Version d’API (ex. `56`)
56
- - `timeout` : Timeout des requêtes (float, secondes)
57
- - `ssl_verify` : Vérification TLS (bool ou chemin CA)
58
-
59
- Fichiers de config : si besoin, passez `config_files` ou `Rundeck.from_config(config_section=...)`.
60
- Variables d'env utiles (config client) :
61
- - `RUNDECK_URL` : URL de base
62
- - `RUNDECK_TOKEN` : token API (en-tête `X-Rundeck-Auth-Token`)
63
- - `RUNDECK_USERNAME` / `RUNDECK_PASSWORD` : auth session (si pas de token)
64
- - `RUNDECK_API_VERSION` : version d'API (ex: 56)
65
- - `RUNDECK_TIMEOUT` : timeout des requêtes (secondes)
66
- - `RUNDECK_SSL_VERIFY` : vérification TLS (bool ou chemin CA)
67
- - `RUNDECK_USER_AGENT` : User-Agent HTTP
68
-
69
- Ressources disponibles
70
- ----------------------
71
- - `projects` (`ProjectManager`) : CRUD projets, export/import de jobs, config projet, archive (export/import).
72
- - `jobs` (`JobManager`) : liste, get, suppressions, actions bulk, exécution.
73
- - `executions` (`ExecutionManager`) : liste/filtre, running, get/delete, query avancée.
74
- - `tokens` (`TokenManager`) : liste, get, create, delete.
75
- - `users` (`UserManager`) : opérations utilisateurs (selon implémentation courante).
76
- - `metrics` (`MetricsManager`) : endpoints `/metrics` (list/data/healthcheck/ping).
77
- - `plugins` (`PluginManager`) : liste des plugins installés (`/plugin/list`).
78
- - `webhooks` (`WebhookEventManager` + `ProjectWebhookManager`) : gestion des webhooks projet et envoi via token.
79
- - `key_storage` (`StorageKeyManager`) : gestion du stockage des clés `/storage/keys`.
80
- - `adhoc` (`AdhocManager`, via `project.adhoc`) : exécution de commandes/scripts AdHoc.
81
- - `system` (`SystemManager`) : info système, exécutions enable/disable, logstorage, ACL.
82
- - `config_management` (`ConfigManagementManager`) : configuration globale `/config`.
83
- - `scm` (via `project.scm` et `job.scm`) : plugins import/export, setup, enable/disable, statut, actions (commit/import/export...).
84
-
85
- Exemples par ressource (complets)
86
- ---------------------------------
87
- Projets
51
+ Configuration follows a cascading model (args > env > files > defaults) via `RundeckConfig`.
52
+
53
+ Main parameters:
54
+ - `url`: Rundeck URL (e.g. `https://rundeck.example.com`)
55
+ - `token`: API token (header `X-Rundeck-Auth-Token`)
56
+ - `username` / `password`: Session authentication (j_security_check) if no token is provided.
57
+ - `api_version`: API version (e.g. `56`)
58
+ - `timeout`: Request timeout (float, seconds)
59
+ - `ssl_verify`: TLS verification (bool or CA path)
60
+
61
+ Config files: if needed, pass `config_files` or `Rundeck.from_config(config_section=...)`.
62
+ Useful env vars (client config):
63
+ - `RUNDECK_URL`: Base URL
64
+ - `RUNDECK_TOKEN`: API token (header `X-Rundeck-Auth-Token`)
65
+ - `RUNDECK_USERNAME` / `RUNDECK_PASSWORD`: Session auth (if no token)
66
+ - `RUNDECK_API_VERSION`: API version (e.g. 56)
67
+ - `RUNDECK_TIMEOUT`: Request timeout (seconds)
68
+ - `RUNDECK_SSL_VERIFY`: TLS verification (bool or CA path)
69
+ - `RUNDECK_USER_AGENT`: HTTP User-Agent
70
+
71
+ Available resources
72
+ -------------------
73
+ - `projects` (`ProjectManager`): CRUD projects, job export/import, project config, archive (export/import).
74
+ - `jobs` (`JobManager`): list, get, deletions, bulk actions, execution.
75
+ - `executions` (`ExecutionManager`): list/filter, running, get/delete, advanced query.
76
+ - `tokens` (`TokenManager`): list, get, create, delete.
77
+ - `users` (`UserManager`): user operations (per current implementation).
78
+ - `metrics` (`MetricsManager`): `/metrics` endpoints (list/data/healthcheck/ping).
79
+ - `plugins` (`PluginManager`): list installed plugins (`/plugin/list`).
80
+ - `webhooks` (`WebhookEventManager` + `ProjectWebhookManager`): project webhook management and sending via token.
81
+ - `key_storage` (`StorageKeyManager`): key storage management `/storage/keys`.
82
+ - `adhoc` (`AdhocManager`, via `project.adhoc`): run AdHoc commands/scripts.
83
+ - `system` (`SystemManager`): system info, executions enable/disable, logstorage, ACL.
84
+ - `config_management` (`ConfigManagementManager`): global configuration `/config`.
85
+ - `scm` (via `project.scm` and `job.scm`): import/export plugins, setup, enable/disable, status, actions (commit/import/export...).
86
+
87
+ Examples by resource (complete)
88
+ -------------------------------
89
+ Projects
88
90
  ```python
89
- # CRUD projet
91
+ # CRUD project
90
92
  p = rd.projects.create("demo")
91
93
  p = rd.projects.get("demo")
92
94
  rd.projects.delete("demo")
93
95
  projects = rd.projects.list()
94
96
 
95
- # Export / import de jobs d'un projet (via le manager de jobs)
97
+ # Export / import jobs from a project (via job manager)
96
98
  project.jobs.export(format="json", idlist="id1,id2", groupPath="group/sub")
97
99
  project.jobs.import_jobs(content=open("jobs.json").read(), format="json", dupeOption="update", uuidOption="remove")
98
100
 
99
- # Export / import du projet (archive ZIP)
101
+ # Project export / import (ZIP archive)
100
102
  archive = project.archive
101
- resp = archive.export(export_all=False, export_webhooks=True) # Response brute (zip)
103
+ resp = archive.export(export_all=False, export_webhooks=True) # Raw response (zip)
102
104
  token_info = archive.export_async(exportAll=True)
103
105
  status = archive.export_status(token_info.get("token", ""))
104
106
  zip_resp = archive.export_download(token_info.get("token", ""))
105
107
 
106
- # Import d'archive (synchrone ou async)
108
+ # Archive import (sync or async)
107
109
  archive.import_archive(
108
110
  content=open("project-export.zip", "rb").read(),
109
111
  jobUuidOption="preserve",
@@ -113,16 +115,16 @@ archive.import_archive(
113
115
  archive.import_archive(content=open("project-export.zip", "rb").read(), async_import=True)
114
116
  archive.import_status()
115
117
 
116
- # Readme / MOTD du projet
117
- project.readme.get_readme() # texte par défaut
118
+ # Project README / MOTD
119
+ project.readme.get_readme() # default text
118
120
  project.readme.get_readme(accept="application/json")
119
- project.readme.update_readme("Nouveau contenu", content_type="text/plain")
121
+ project.readme.update_readme("New content", content_type="text/plain")
120
122
  project.readme.delete_readme()
121
123
  project.readme.get_motd()
122
- project.readme.update_motd("Message du jour", content_type="text/plain")
124
+ project.readme.update_motd("Message of the day", content_type="text/plain")
123
125
  project.readme.delete_motd()
124
126
 
125
- # Config projet (clé/valeur)
127
+ # Project config (key/value)
126
128
  conf = p.config.get()
127
129
  p.config.keys.get("project.label")
128
130
  p.config.keys.set("project.label", "Demo")
@@ -130,27 +132,27 @@ p.config.keys.update({"project.description": "Sample"})
130
132
  p.config.replace({"project.label": "Demo", "project.description": "Sample"})
131
133
  p.config.keys.delete("project.label")
132
134
 
133
- # SCM import/export (sur un projet)
134
- scm_import = project.scm.import_ # ou getattr(project.scm, "import")
135
+ # SCM import/export (on a project)
136
+ scm_import = project.scm.import_ # or getattr(project.scm, "import")
135
137
  scm_export = project.scm.export
136
138
 
137
- # Découverte des plugins
139
+ # Plugin discovery
138
140
  scm_import.plugins.list()
139
141
  scm_export.plugins.list()
140
142
 
141
- # Champs d'entrée pour un plugin et setup (plugin_type explicite)
143
+ # Input fields for a plugin and setup (explicit plugin_type)
142
144
  fields = scm_import.plugins.input_fields("git-import")
143
145
  scm_import.config.setup("git-import", {"url": "ssh://git@example.com/repo.git", "dir": "/tmp/repo"})
144
146
 
145
- # Activer/désactiver un plugin
147
+ # Enable/disable a plugin
146
148
  scm_import.config.enable("git-import")
147
149
  scm_export.config.disable("custom-export")
148
150
 
149
- # Statut/config SCM
151
+ # SCM status/config
150
152
  import_status = scm_import.actions.status()
151
153
  export_conf = scm_export.config.get()
152
154
 
153
- # Actions SCM côté projet (ex: commit/pull/push selon plugin)
155
+ # Project-side SCM actions (e.g., commit/pull/push depending on plugin)
154
156
  action_fields = scm_export.actions.input_fields("commit")
155
157
  scm_export.actions.perform(
156
158
  "commit",
@@ -163,14 +165,14 @@ scm_export.actions.perform(
163
165
 
164
166
  Jobs
165
167
  ```python
166
- # Lister les jobs d’un projet
168
+ # List jobs for a project
167
169
  jobs = rd.jobs.list(project="demo", groupPath="ops")
168
170
 
169
- # Depuis un projet parenté
171
+ # From a parent project
170
172
  project = rd.projects.get("demo")
171
173
  jobs = project.jobs.list()
172
174
 
173
- # Accès direct à un job
175
+ # Direct access to a job
174
176
  job = rd.jobs.get("job-id")
175
177
  job.delete()
176
178
  job.definition(format="yaml")
@@ -185,7 +187,7 @@ tags = job.tags()
185
187
  workflow = job.workflow()
186
188
  forecast = job.forecast(time="2024-05-01T10:00:00Z", max=5)
187
189
 
188
- # Exporter/importer des jobs via le manager (paramètre project ou parent)
190
+ # Export/import jobs via manager (project parameter or parent)
189
191
  rd.jobs.export(project="demo", format="xml", idlist="id1,id2", groupPath="group")
190
192
  rd.jobs.import_jobs(
191
193
  project="demo",
@@ -193,30 +195,30 @@ rd.jobs.import_jobs(
193
195
  fileformat="xml",
194
196
  dupeOption="update",
195
197
  )
196
- # Ou via un projet parenté
198
+ # Or via a parent project
197
199
  project.jobs.export(format="json")
198
200
  project.jobs.import_jobs(content=open("jobs.json", "rb").read(), fileformat="json")
199
201
 
200
- Note: l'import reste exposé sous le nom `import_jobs(...)` (le mot-clé Python empêche un appel direct à `.import`). Si vous préférez l'alias, utilisez `getattr(rd.jobs, "import")(...)`.
202
+ Note: import remains exposed as `import_jobs(...)` (the Python keyword prevents a direct call to `.import`). If you prefer the alias, use `getattr(rd.jobs, "import")(...)`.
201
203
 
202
- # Exécuter et récupérer l'exécution
204
+ # Run and get the execution
203
205
  execution = job.run(argString="-option value")
204
206
 
205
- # Actions bulk
207
+ # Bulk actions
206
208
  rd.jobs.bulk.enable_execution(["id1", "id2"])
207
209
  rd.jobs.bulk.disable_execution(["id1", "id2"])
208
210
  rd.jobs.bulk.delete(["id1", "id2"])
209
211
  rd.jobs.bulk.enable_schedule(["id1", "id2"])
210
212
  rd.jobs.bulk.disable_schedule(["id1", "id2"])
211
213
 
212
- # Upload de fichiers d'option et fichiers uploadés
214
+ # Upload option files and uploaded files
213
215
  job.upload_option_file("csvfile", open("data.csv", "rb").read(), file_name="data.csv")
214
216
  job.list_uploaded_files(max=20)
215
217
  rd.jobs.get_uploaded_file_info("file-id")
216
218
 
217
- # SCM import/export sur un job
219
+ # SCM import/export on a job
218
220
  job_scm_export = job.scm.export
219
- job_scm_import = job.scm.import_ # ou getattr(job.scm, "import")
221
+ job_scm_import = job.scm.import_ # or getattr(job.scm, "import")
220
222
 
221
223
  job_scm_export.status()
222
224
  job_scm_export.diff()
@@ -226,7 +228,7 @@ job_scm_import.status()
226
228
  job_scm_import.input_fields("pull")
227
229
  job_scm_import.perform("pull", input_values={"message": "Update from repo"})
228
230
 
229
- # Ressources d'un projet
231
+ # Project resources
230
232
  resources = project.resources.list(format="json", groupPath="ops")
231
233
  node = project.resources.get("node1")
232
234
  sources = project.sources.list()
@@ -240,18 +242,18 @@ project.acl.update("policy.aclpolicy", content="...yaml...")
240
242
  project.acl.delete("policy.aclpolicy")
241
243
  ```
242
244
 
243
- Exécutions
245
+ Executions
244
246
  ```python
245
- # Liste simple ou paginée
247
+ # Simple or paginated list
246
248
  execs = rd.executions.list(project="demo", status="running", max=50, offset=0)
247
- running = rd.executions.running(project="demo") # ou "*" pour tous
249
+ running = rd.executions.running(project="demo") # or "*" for all
248
250
 
249
- # Détails / suppression
251
+ # Details / deletion
250
252
  e = rd.executions.get("123")
251
253
  rd.executions.delete("123")
252
254
  rd.executions.bulk_delete(["123", "124"])
253
255
 
254
- # Requête avancée (query)
256
+ # Advanced query
255
257
  advanced = rd.executions.query(
256
258
  project="demo",
257
259
  statusFilter="failed",
@@ -261,12 +263,12 @@ advanced = rd.executions.query(
261
263
  max=100,
262
264
  )
263
265
 
264
- # Méthodes sur Execution
266
+ # Execution methods
265
267
  e.abort(asUser="admin")
266
268
  output = e.get_output(offset=0, maxlines=100)
267
269
  state = e.get_state()
268
270
  is_running = e.is_running()
269
- e.refresh() # recharge les données
271
+ e.refresh() # reload data
270
272
  ```
271
273
 
272
274
  Tokens
@@ -278,20 +280,20 @@ new_token = rd.tokens.create(user="alice", roles=["admin"], duration="90d", name
278
280
  rd.tokens.delete(new_token.id)
279
281
  ```
280
282
 
281
- Utilisateurs
283
+ Users
282
284
  ```python
283
285
  users = rd.users.list()
284
286
  me = rd.users.get_current()
285
287
  u = rd.users.get("bob")
286
288
  roles = rd.users.current_roles()
287
289
 
288
- # Mise à jour via manager ou objet
290
+ # Update via manager or object
289
291
  u = rd.users.update("bob", firstName="Bob", lastName="Builder", email="bob@example.com")
290
- u.roles() # via l'objet
291
- u.update(email="new@example.com") # met à jour et rafraîchit l'objet
292
+ u.roles() # via the object
293
+ u.update(email="new@example.com") # updates and refreshes the object
292
294
  ```
293
295
 
294
- Système
296
+ System
295
297
  ```python
296
298
  system = rd.system
297
299
  info = system.info()
@@ -301,7 +303,7 @@ system.logstorage.info()
301
303
  system.logstorage.incomplete(max=50, offset=0)
302
304
  system.logstorage.incomplete_resume()
303
305
 
304
- # Mode exécution (sous-manager)
306
+ # Execution mode (sub-manager)
305
307
  system.executions.enable()
306
308
  system.executions.disable()
307
309
  system.executions.status()
@@ -318,7 +320,7 @@ rd.scheduler.takeover(all_servers=True)
318
320
  rd.scheduler.takeover(server_uuid="uuid-123", project="demo", job_id="job-1")
319
321
  ```
320
322
 
321
- Métriques
323
+ Metrics
322
324
  ```python
323
325
  metrics = rd.metrics
324
326
  metrics.list()
@@ -326,16 +328,16 @@ metrics.data()
326
328
  metrics.healthcheck()
327
329
  metrics.ping()
328
330
 
329
- # Plugins installés
331
+ # Installed plugins
330
332
  plugins = rd.plugins.list()
331
333
  for plugin in plugins:
332
334
  print(plugin.name, plugin.service)
333
335
 
334
- # Détail d'un plugin
336
+ # Plugin details
335
337
  first = plugins[0]
336
338
  detail = rd.plugins.detail(first.service, first.name)
337
339
 
338
- # Webhooks projet et envoi
340
+ # Project webhooks and send
339
341
  project = rd.projects.get("demo")
340
342
  wh = project.webhooks
341
343
  wh.create(
@@ -355,9 +357,9 @@ wh.delete(first_hook.id)
355
357
 
356
358
  # Key storage (/storage/keys)
357
359
  ks = rd.key_storage
358
- # Créer un secret (password)
360
+ # Create a secret (password)
359
361
  ks.create("integration/secret1", content="s3cr3t", content_type="application/x-rundeck-data-password")
360
- resources = ks.list() # liste racine
362
+ resources = ks.list() # root listing
361
363
  meta = ks.get_metadata("integration/secret1")
362
364
  ks.delete("integration/secret1")
363
365
 
@@ -365,17 +367,17 @@ ks.delete("integration/secret1")
365
367
  project = rd.projects.get("demo")
366
368
  exec1 = project.adhoc.run_command("echo 'hello world'")
367
369
  exec2 = project.adhoc.run_script("echo 'from script'")
368
- # exec2.id pour suivre l'exécution
369
- # Mode stub (sans refresh immédiat) puis refresh manuel
370
+ # exec2.id to track execution
371
+ # Stub mode (no immediate refresh) then manual refresh
370
372
  exec_stub = project.adhoc.run_command("echo stub", refresh=False)
371
- exec_stub.refresh() # charge l'objet complet
373
+ exec_stub.refresh() # load the full object
372
374
  # Script via multipart (upload)
373
375
  exec3 = project.adhoc.run_script(
374
376
  script_file=("hello.sh", "echo multipart", "text/plain"),
375
377
  refresh=False,
376
378
  )
377
379
 
378
- # Features système
380
+ # System features
379
381
  features = rd.features.list()
380
382
  if features:
381
383
  first = features[0]
@@ -383,7 +385,7 @@ if features:
383
385
  print(first.name, status.enabled)
384
386
  ```
385
387
 
386
- Configuration globale `/config`
388
+ Global configuration `/config`
387
389
  ```python
388
390
  cfg = rd.config_management
389
391
  all_configs = cfg.list()
@@ -395,26 +397,27 @@ cfg.restart()
395
397
 
396
398
  Pagination
397
399
  ----------
398
- Les managers héritent de `RundeckObjectManager.iter(...)` (offset/max). Exemple :
400
+ Managers inherit `RundeckObjectManager.iter(...)` (offset/max). Example:
399
401
  ```python
400
402
  for job in rd.jobs.iter(project="demo", page_size=100):
401
403
  print(job.id)
402
404
  ```
403
405
 
404
- Gestion des erreurs
405
- -------------------
406
- Les erreurs HTTP passent par `raise_for_status` et lèvent des exceptions dédiées (ex. `RundeckAuthenticationError`, `RundeckNotFoundError`, `RundeckValidationError`, `RundeckConflictError`, `RundeckServerError`). Gérez-les avec un bloc `try/except` autour des appels client.
407
-
408
- Développement et tests
409
- ----------------------
410
- - Formatage/Lint : `black src/rundeck tests/`, `ruff check src/rundeck tests/`
411
- - Typage : `mypy src/rundeck/`
412
- - Tests : `pytest`
413
- - Tests d'intégration locaux : `scripts/run-integration.sh` (démarre docker compose à la racine, exporte par défaut `RUNDECK_URL=http://localhost:4440`, `RUNDECK_TOKEN=adminToken`, `RUNDECK_API_VERSION=56`, attend le healthcheck, lance `poetry run pytest -m integration`). Vous pouvez surcharger les variables d'environnement avant l'appel. Ajoutez `KEEP_STACK=1` pour ne pas arrêter l'instance après les tests. Script de teardown dédié : `scripts/stop-integration.sh`.
414
-
415
- Structure du code
416
- -----------------
417
- - `src/rundeck/base.py` : objets/manager génériques, helpers `_build_path`, pagination, CRUD.
418
- - `src/rundeck/client.py` : client HTTP, méthodes `http_get/post/put/delete/list`.
419
- - `src/rundeck/v1/objects/` : managers et objets métiers (projects, jobs, executions, system, tokens, users, config_management).
420
- - Schéma managers/objets : on applique le pattern manager → objet partout où l’API expose une ressource identifiable (jobs, projects, users, tokens, executions, etc.). Pour les endpoints purement globaux ou utilitaires sans ressource (ex. `/system` et ses sous-domaines, `/config`, `/metrics`), on utilise des sous-managers dédiés plutôt que de forcer un objet artificiel.
406
+ Error handling
407
+ --------------
408
+ HTTP errors go through `raise_for_status` and raise dedicated exceptions (e.g., `RundeckAuthenticationError`, `RundeckNotFoundError`, `RundeckValidationError`, `RundeckConflictError`, `RundeckServerError`). Handle them with a `try/except` block around client calls.
409
+
410
+ Development and testing
411
+ -----------------------
412
+ - Development setup (contributors): clone the repo and run `poetry install`.
413
+ - Formatting/Lint: `black src/rundeck tests/`, `ruff check src/rundeck tests/`
414
+ - Typing: `mypy src/rundeck/`
415
+ - Tests: `pytest`
416
+ - Local integration tests: `scripts/run-integration.sh` (starts docker compose at repo root, exports by default `RUNDECK_URL=http://localhost:4440`, `RUNDECK_TOKEN=adminToken`, `RUNDECK_API_VERSION=56`, waits for the healthcheck, runs `poetry run pytest -m integration`). You can override env vars before running. Add `KEEP_STACK=1` to keep the instance running after tests. Dedicated teardown script: `scripts/stop-integration.sh`.
417
+
418
+ Code structure
419
+ --------------
420
+ - `src/rundeck/base.py`: generic objects/managers, `_build_path` helpers, pagination, CRUD.
421
+ - `src/rundeck/client.py`: HTTP client, `http_get/post/put/delete/list` methods.
422
+ - `src/rundeck/v1/objects/`: domain managers and objects (projects, jobs, executions, system, tokens, users, config_management).
423
+ - Managers/objects schema: we apply the manager -> object pattern wherever the API exposes an identifiable resource (jobs, projects, users, tokens, executions, etc.). For purely global or utility endpoints without a resource (e.g., `/system` and its subdomains, `/config`, `/metrics`), we use dedicated sub-managers rather than forcing an artificial object.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-rundeck"
3
- version = "0.1.0"
3
+ version = "0.3.0"
4
4
  description = "Python client for the Rundeck API (v14-v56)."
5
5
  authors = [
6
6
  {name = "Pascal Seckinger",email = "pascal.seckinger@protonmail.com"}
@@ -26,6 +26,13 @@ dependencies = [
26
26
  "requests (>=2.32.5,<3.0.0)"
27
27
  ]
28
28
 
29
+ [project.urls]
30
+ Homepage = "https://gitlab.com/pascal.seckinger/python-rundeck"
31
+ Repository = "https://gitlab.com/pascal.seckinger/python-rundeck"
32
+ Documentation = "https://gitlab.com/pascal.seckinger/python-rundeck/-/blob/main/README.md"
33
+ Issues = "https://gitlab.com/pascal.seckinger/python-rundeck/-/issues"
34
+ Changelog = "https://gitlab.com/pascal.seckinger/python-rundeck/-/blob/main/CHANGELOG.md"
35
+
29
36
  [project.optional-dependencies]
30
37
  dev = [
31
38
  "bandit (>=1.7.9,<2.0.0)",
File without changes