python-rundeck 0.2.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.2.0 → python_rundeck-0.3.0}/PKG-INFO +130 -127
  2. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/README.md +129 -126
  3. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/pyproject.toml +1 -1
  4. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/LICENSE +0 -0
  5. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/__init__.py +0 -0
  6. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/base.py +0 -0
  7. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/client.py +0 -0
  8. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/config.py +0 -0
  9. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/const.py +0 -0
  10. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/exceptions.py +0 -0
  11. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/py.typed +0 -0
  12. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/__init__.py +0 -0
  13. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/__init__.py +0 -0
  14. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/adhoc.py +0 -0
  15. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/config_management.py +0 -0
  16. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/executions.py +0 -0
  17. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/features.py +0 -0
  18. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/jobs.py +0 -0
  19. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/key_storage.py +0 -0
  20. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/metrics.py +0 -0
  21. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/plugins.py +0 -0
  22. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/projects.py +0 -0
  23. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/scheduler.py +0 -0
  24. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/scm.py +0 -0
  25. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/system.py +0 -0
  26. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/tokens.py +0 -0
  27. {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/users.py +0 -0
  28. {python_rundeck-0.2.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.2.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
@@ -38,44 +38,46 @@ Description-Content-Type: text/markdown
38
38
 
39
39
  python-rundeck
40
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)
41
45
 
42
- 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).
43
47
 
44
- Sommaire
48
+ Contents
45
49
  --------
46
50
  - Installation
47
- - Démarrage rapide
51
+ - Quick start
48
52
  - Configuration
49
- - Ressources disponibles
50
- - Exemples par ressource
51
- - Gestion des erreurs
52
- - Développement et tests
53
+ - Available resources
54
+ - Examples by resource
55
+ - Error handling
56
+ - Development and testing
53
57
 
54
58
  Installation
55
59
  ------------
56
- Prerequis : Python 3.11+. Le projet utilise Poetry.
60
+ Prerequisites: Python 3.11+. Install from PyPI.
57
61
 
58
62
  ```bash
59
- poetry install
60
- # ou en editable avec pip
61
- pip install -e .
63
+ pip install python-rundeck
62
64
  ```
63
65
 
64
- Démarrage rapide
65
- ----------------
66
+ Quick start
67
+ -----------
66
68
  ```python
67
69
  from rundeck.client import Rundeck
68
70
 
69
71
  rd = Rundeck(url="https://rundeck.example.com", token="MY_TOKEN", api_version=56)
70
- # Auth par mot de passe (session cookie) si pas de token
72
+ # Password auth (session cookie) if no token
71
73
  rd = Rundeck(url="https://rundeck.example.com", username="admin", password="admin", api_version=56)
72
74
 
73
- # Lister les projets
75
+ # List projects
74
76
  projects = rd.projects.list()
75
77
  for p in projects:
76
78
  print(p.name)
77
79
 
78
- # Récupérer un projet et lancer un job
80
+ # Get a project and run a job
79
81
  project = rd.projects.get("demo")
80
82
  jobs = project.jobs.list()
81
83
  execu = jobs[0].run()
@@ -84,64 +86,64 @@ print(execu.id)
84
86
 
85
87
  Configuration
86
88
  -------------
87
- La configuration suit un modèle en cascade (args > env > fichiers > défauts) via `RundeckConfig`.
88
-
89
- Paramètres principaux :
90
- - `url` : URL Rundeck (ex. `https://rundeck.example.com`)
91
- - `token` : Token API (en-tête `X-Rundeck-Auth-Token`)
92
- - `username` / `password` : Authentification par session (j_security_check) si aucun token n'est fourni.
93
- - `api_version` : Version d’API (ex. `56`)
94
- - `timeout` : Timeout des requêtes (float, secondes)
95
- - `ssl_verify` : Vérification TLS (bool ou chemin CA)
96
-
97
- Fichiers de config : si besoin, passez `config_files` ou `Rundeck.from_config(config_section=...)`.
98
- Variables d'env utiles (config client) :
99
- - `RUNDECK_URL` : URL de base
100
- - `RUNDECK_TOKEN` : token API (en-tête `X-Rundeck-Auth-Token`)
101
- - `RUNDECK_USERNAME` / `RUNDECK_PASSWORD` : auth session (si pas de token)
102
- - `RUNDECK_API_VERSION` : version d'API (ex: 56)
103
- - `RUNDECK_TIMEOUT` : timeout des requêtes (secondes)
104
- - `RUNDECK_SSL_VERIFY` : vérification TLS (bool ou chemin CA)
105
- - `RUNDECK_USER_AGENT` : User-Agent HTTP
106
-
107
- Ressources disponibles
108
- ----------------------
109
- - `projects` (`ProjectManager`) : CRUD projets, export/import de jobs, config projet, archive (export/import).
110
- - `jobs` (`JobManager`) : liste, get, suppressions, actions bulk, exécution.
111
- - `executions` (`ExecutionManager`) : liste/filtre, running, get/delete, query avancée.
112
- - `tokens` (`TokenManager`) : liste, get, create, delete.
113
- - `users` (`UserManager`) : opérations utilisateurs (selon implémentation courante).
114
- - `metrics` (`MetricsManager`) : endpoints `/metrics` (list/data/healthcheck/ping).
115
- - `plugins` (`PluginManager`) : liste des plugins installés (`/plugin/list`).
116
- - `webhooks` (`WebhookEventManager` + `ProjectWebhookManager`) : gestion des webhooks projet et envoi via token.
117
- - `key_storage` (`StorageKeyManager`) : gestion du stockage des clés `/storage/keys`.
118
- - `adhoc` (`AdhocManager`, via `project.adhoc`) : exécution de commandes/scripts AdHoc.
119
- - `system` (`SystemManager`) : info système, exécutions enable/disable, logstorage, ACL.
120
- - `config_management` (`ConfigManagementManager`) : configuration globale `/config`.
121
- - `scm` (via `project.scm` et `job.scm`) : plugins import/export, setup, enable/disable, statut, actions (commit/import/export...).
122
-
123
- Exemples par ressource (complets)
124
- ---------------------------------
125
- 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
126
128
  ```python
127
- # CRUD projet
129
+ # CRUD project
128
130
  p = rd.projects.create("demo")
129
131
  p = rd.projects.get("demo")
130
132
  rd.projects.delete("demo")
131
133
  projects = rd.projects.list()
132
134
 
133
- # Export / import de jobs d'un projet (via le manager de jobs)
135
+ # Export / import jobs from a project (via job manager)
134
136
  project.jobs.export(format="json", idlist="id1,id2", groupPath="group/sub")
135
137
  project.jobs.import_jobs(content=open("jobs.json").read(), format="json", dupeOption="update", uuidOption="remove")
136
138
 
137
- # Export / import du projet (archive ZIP)
139
+ # Project export / import (ZIP archive)
138
140
  archive = project.archive
139
- 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)
140
142
  token_info = archive.export_async(exportAll=True)
141
143
  status = archive.export_status(token_info.get("token", ""))
142
144
  zip_resp = archive.export_download(token_info.get("token", ""))
143
145
 
144
- # Import d'archive (synchrone ou async)
146
+ # Archive import (sync or async)
145
147
  archive.import_archive(
146
148
  content=open("project-export.zip", "rb").read(),
147
149
  jobUuidOption="preserve",
@@ -151,16 +153,16 @@ archive.import_archive(
151
153
  archive.import_archive(content=open("project-export.zip", "rb").read(), async_import=True)
152
154
  archive.import_status()
153
155
 
154
- # Readme / MOTD du projet
155
- project.readme.get_readme() # texte par défaut
156
+ # Project README / MOTD
157
+ project.readme.get_readme() # default text
156
158
  project.readme.get_readme(accept="application/json")
157
- project.readme.update_readme("Nouveau contenu", content_type="text/plain")
159
+ project.readme.update_readme("New content", content_type="text/plain")
158
160
  project.readme.delete_readme()
159
161
  project.readme.get_motd()
160
- project.readme.update_motd("Message du jour", content_type="text/plain")
162
+ project.readme.update_motd("Message of the day", content_type="text/plain")
161
163
  project.readme.delete_motd()
162
164
 
163
- # Config projet (clé/valeur)
165
+ # Project config (key/value)
164
166
  conf = p.config.get()
165
167
  p.config.keys.get("project.label")
166
168
  p.config.keys.set("project.label", "Demo")
@@ -168,27 +170,27 @@ p.config.keys.update({"project.description": "Sample"})
168
170
  p.config.replace({"project.label": "Demo", "project.description": "Sample"})
169
171
  p.config.keys.delete("project.label")
170
172
 
171
- # SCM import/export (sur un projet)
172
- 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")
173
175
  scm_export = project.scm.export
174
176
 
175
- # Découverte des plugins
177
+ # Plugin discovery
176
178
  scm_import.plugins.list()
177
179
  scm_export.plugins.list()
178
180
 
179
- # Champs d'entrée pour un plugin et setup (plugin_type explicite)
181
+ # Input fields for a plugin and setup (explicit plugin_type)
180
182
  fields = scm_import.plugins.input_fields("git-import")
181
183
  scm_import.config.setup("git-import", {"url": "ssh://git@example.com/repo.git", "dir": "/tmp/repo"})
182
184
 
183
- # Activer/désactiver un plugin
185
+ # Enable/disable a plugin
184
186
  scm_import.config.enable("git-import")
185
187
  scm_export.config.disable("custom-export")
186
188
 
187
- # Statut/config SCM
189
+ # SCM status/config
188
190
  import_status = scm_import.actions.status()
189
191
  export_conf = scm_export.config.get()
190
192
 
191
- # Actions SCM côté projet (ex: commit/pull/push selon plugin)
193
+ # Project-side SCM actions (e.g., commit/pull/push depending on plugin)
192
194
  action_fields = scm_export.actions.input_fields("commit")
193
195
  scm_export.actions.perform(
194
196
  "commit",
@@ -201,14 +203,14 @@ scm_export.actions.perform(
201
203
 
202
204
  Jobs
203
205
  ```python
204
- # Lister les jobs d’un projet
206
+ # List jobs for a project
205
207
  jobs = rd.jobs.list(project="demo", groupPath="ops")
206
208
 
207
- # Depuis un projet parenté
209
+ # From a parent project
208
210
  project = rd.projects.get("demo")
209
211
  jobs = project.jobs.list()
210
212
 
211
- # Accès direct à un job
213
+ # Direct access to a job
212
214
  job = rd.jobs.get("job-id")
213
215
  job.delete()
214
216
  job.definition(format="yaml")
@@ -223,7 +225,7 @@ tags = job.tags()
223
225
  workflow = job.workflow()
224
226
  forecast = job.forecast(time="2024-05-01T10:00:00Z", max=5)
225
227
 
226
- # Exporter/importer des jobs via le manager (paramètre project ou parent)
228
+ # Export/import jobs via manager (project parameter or parent)
227
229
  rd.jobs.export(project="demo", format="xml", idlist="id1,id2", groupPath="group")
228
230
  rd.jobs.import_jobs(
229
231
  project="demo",
@@ -231,30 +233,30 @@ rd.jobs.import_jobs(
231
233
  fileformat="xml",
232
234
  dupeOption="update",
233
235
  )
234
- # Ou via un projet parenté
236
+ # Or via a parent project
235
237
  project.jobs.export(format="json")
236
238
  project.jobs.import_jobs(content=open("jobs.json", "rb").read(), fileformat="json")
237
239
 
238
- 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")(...)`.
239
241
 
240
- # Exécuter et récupérer l'exécution
242
+ # Run and get the execution
241
243
  execution = job.run(argString="-option value")
242
244
 
243
- # Actions bulk
245
+ # Bulk actions
244
246
  rd.jobs.bulk.enable_execution(["id1", "id2"])
245
247
  rd.jobs.bulk.disable_execution(["id1", "id2"])
246
248
  rd.jobs.bulk.delete(["id1", "id2"])
247
249
  rd.jobs.bulk.enable_schedule(["id1", "id2"])
248
250
  rd.jobs.bulk.disable_schedule(["id1", "id2"])
249
251
 
250
- # Upload de fichiers d'option et fichiers uploadés
252
+ # Upload option files and uploaded files
251
253
  job.upload_option_file("csvfile", open("data.csv", "rb").read(), file_name="data.csv")
252
254
  job.list_uploaded_files(max=20)
253
255
  rd.jobs.get_uploaded_file_info("file-id")
254
256
 
255
- # SCM import/export sur un job
257
+ # SCM import/export on a job
256
258
  job_scm_export = job.scm.export
257
- job_scm_import = job.scm.import_ # ou getattr(job.scm, "import")
259
+ job_scm_import = job.scm.import_ # or getattr(job.scm, "import")
258
260
 
259
261
  job_scm_export.status()
260
262
  job_scm_export.diff()
@@ -264,7 +266,7 @@ job_scm_import.status()
264
266
  job_scm_import.input_fields("pull")
265
267
  job_scm_import.perform("pull", input_values={"message": "Update from repo"})
266
268
 
267
- # Ressources d'un projet
269
+ # Project resources
268
270
  resources = project.resources.list(format="json", groupPath="ops")
269
271
  node = project.resources.get("node1")
270
272
  sources = project.sources.list()
@@ -278,18 +280,18 @@ project.acl.update("policy.aclpolicy", content="...yaml...")
278
280
  project.acl.delete("policy.aclpolicy")
279
281
  ```
280
282
 
281
- Exécutions
283
+ Executions
282
284
  ```python
283
- # Liste simple ou paginée
285
+ # Simple or paginated list
284
286
  execs = rd.executions.list(project="demo", status="running", max=50, offset=0)
285
- running = rd.executions.running(project="demo") # ou "*" pour tous
287
+ running = rd.executions.running(project="demo") # or "*" for all
286
288
 
287
- # Détails / suppression
289
+ # Details / deletion
288
290
  e = rd.executions.get("123")
289
291
  rd.executions.delete("123")
290
292
  rd.executions.bulk_delete(["123", "124"])
291
293
 
292
- # Requête avancée (query)
294
+ # Advanced query
293
295
  advanced = rd.executions.query(
294
296
  project="demo",
295
297
  statusFilter="failed",
@@ -299,12 +301,12 @@ advanced = rd.executions.query(
299
301
  max=100,
300
302
  )
301
303
 
302
- # Méthodes sur Execution
304
+ # Execution methods
303
305
  e.abort(asUser="admin")
304
306
  output = e.get_output(offset=0, maxlines=100)
305
307
  state = e.get_state()
306
308
  is_running = e.is_running()
307
- e.refresh() # recharge les données
309
+ e.refresh() # reload data
308
310
  ```
309
311
 
310
312
  Tokens
@@ -316,20 +318,20 @@ new_token = rd.tokens.create(user="alice", roles=["admin"], duration="90d", name
316
318
  rd.tokens.delete(new_token.id)
317
319
  ```
318
320
 
319
- Utilisateurs
321
+ Users
320
322
  ```python
321
323
  users = rd.users.list()
322
324
  me = rd.users.get_current()
323
325
  u = rd.users.get("bob")
324
326
  roles = rd.users.current_roles()
325
327
 
326
- # Mise à jour via manager ou objet
328
+ # Update via manager or object
327
329
  u = rd.users.update("bob", firstName="Bob", lastName="Builder", email="bob@example.com")
328
- u.roles() # via l'objet
329
- 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
330
332
  ```
331
333
 
332
- Système
334
+ System
333
335
  ```python
334
336
  system = rd.system
335
337
  info = system.info()
@@ -339,7 +341,7 @@ system.logstorage.info()
339
341
  system.logstorage.incomplete(max=50, offset=0)
340
342
  system.logstorage.incomplete_resume()
341
343
 
342
- # Mode exécution (sous-manager)
344
+ # Execution mode (sub-manager)
343
345
  system.executions.enable()
344
346
  system.executions.disable()
345
347
  system.executions.status()
@@ -356,7 +358,7 @@ rd.scheduler.takeover(all_servers=True)
356
358
  rd.scheduler.takeover(server_uuid="uuid-123", project="demo", job_id="job-1")
357
359
  ```
358
360
 
359
- Métriques
361
+ Metrics
360
362
  ```python
361
363
  metrics = rd.metrics
362
364
  metrics.list()
@@ -364,16 +366,16 @@ metrics.data()
364
366
  metrics.healthcheck()
365
367
  metrics.ping()
366
368
 
367
- # Plugins installés
369
+ # Installed plugins
368
370
  plugins = rd.plugins.list()
369
371
  for plugin in plugins:
370
372
  print(plugin.name, plugin.service)
371
373
 
372
- # Détail d'un plugin
374
+ # Plugin details
373
375
  first = plugins[0]
374
376
  detail = rd.plugins.detail(first.service, first.name)
375
377
 
376
- # Webhooks projet et envoi
378
+ # Project webhooks and send
377
379
  project = rd.projects.get("demo")
378
380
  wh = project.webhooks
379
381
  wh.create(
@@ -393,9 +395,9 @@ wh.delete(first_hook.id)
393
395
 
394
396
  # Key storage (/storage/keys)
395
397
  ks = rd.key_storage
396
- # Créer un secret (password)
398
+ # Create a secret (password)
397
399
  ks.create("integration/secret1", content="s3cr3t", content_type="application/x-rundeck-data-password")
398
- resources = ks.list() # liste racine
400
+ resources = ks.list() # root listing
399
401
  meta = ks.get_metadata("integration/secret1")
400
402
  ks.delete("integration/secret1")
401
403
 
@@ -403,17 +405,17 @@ ks.delete("integration/secret1")
403
405
  project = rd.projects.get("demo")
404
406
  exec1 = project.adhoc.run_command("echo 'hello world'")
405
407
  exec2 = project.adhoc.run_script("echo 'from script'")
406
- # exec2.id pour suivre l'exécution
407
- # Mode stub (sans refresh immédiat) puis refresh manuel
408
+ # exec2.id to track execution
409
+ # Stub mode (no immediate refresh) then manual refresh
408
410
  exec_stub = project.adhoc.run_command("echo stub", refresh=False)
409
- exec_stub.refresh() # charge l'objet complet
411
+ exec_stub.refresh() # load the full object
410
412
  # Script via multipart (upload)
411
413
  exec3 = project.adhoc.run_script(
412
414
  script_file=("hello.sh", "echo multipart", "text/plain"),
413
415
  refresh=False,
414
416
  )
415
417
 
416
- # Features système
418
+ # System features
417
419
  features = rd.features.list()
418
420
  if features:
419
421
  first = features[0]
@@ -421,7 +423,7 @@ if features:
421
423
  print(first.name, status.enabled)
422
424
  ```
423
425
 
424
- Configuration globale `/config`
426
+ Global configuration `/config`
425
427
  ```python
426
428
  cfg = rd.config_management
427
429
  all_configs = cfg.list()
@@ -433,27 +435,28 @@ cfg.restart()
433
435
 
434
436
  Pagination
435
437
  ----------
436
- Les managers héritent de `RundeckObjectManager.iter(...)` (offset/max). Exemple :
438
+ Managers inherit `RundeckObjectManager.iter(...)` (offset/max). Example:
437
439
  ```python
438
440
  for job in rd.jobs.iter(project="demo", page_size=100):
439
441
  print(job.id)
440
442
  ```
441
443
 
442
- Gestion des erreurs
443
- -------------------
444
- 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.
445
-
446
- Développement et tests
447
- ----------------------
448
- - Formatage/Lint : `black src/rundeck tests/`, `ruff check src/rundeck tests/`
449
- - Typage : `mypy src/rundeck/`
450
- - Tests : `pytest`
451
- - 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`.
452
-
453
- Structure du code
454
- -----------------
455
- - `src/rundeck/base.py` : objets/manager génériques, helpers `_build_path`, pagination, CRUD.
456
- - `src/rundeck/client.py` : client HTTP, méthodes `http_get/post/put/delete/list`.
457
- - `src/rundeck/v1/objects/` : managers et objets métiers (projects, jobs, executions, system, tokens, users, config_management).
458
- - 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.
459
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.2.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"}
File without changes