primitive 0.1.13__tar.gz → 0.1.15__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 (58) hide show
  1. {primitive-0.1.13 → primitive-0.1.15}/PKG-INFO +1 -1
  2. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/__about__.py +1 -1
  3. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/agent/actions.py +55 -24
  4. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/cli.py +8 -0
  5. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/client.py +6 -2
  6. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/daemons/launch_agents.py +11 -1
  7. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/daemons/launch_service.py +10 -1
  8. {primitive-0.1.13/src/primitive/projects → primitive-0.1.15/src/primitive/jobs}/actions.py +137 -5
  9. primitive-0.1.15/src/primitive/jobs/commands.py +41 -0
  10. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/lint/actions.py +1 -1
  11. primitive-0.1.15/src/primitive/organizations/actions.py +84 -0
  12. primitive-0.1.15/src/primitive/organizations/commands.py +24 -0
  13. primitive-0.1.15/src/primitive/projects/actions.py +78 -0
  14. primitive-0.1.15/src/primitive/projects/commands.py +24 -0
  15. primitive-0.1.15/src/primitive/sim/actions.py +190 -0
  16. primitive-0.1.15/src/primitive/sim/commands.py +19 -0
  17. primitive-0.1.15/src/primitive/sim/vcd.py +761 -0
  18. primitive-0.1.13/src/primitive/simulations/actions.py +0 -48
  19. {primitive-0.1.13 → primitive-0.1.15}/.git-hooks/pre-commit +0 -0
  20. {primitive-0.1.13 → primitive-0.1.15}/.gitattributes +0 -0
  21. {primitive-0.1.13 → primitive-0.1.15}/.github/workflows/lint.yml +0 -0
  22. {primitive-0.1.13 → primitive-0.1.15}/.github/workflows/publish.yml +0 -0
  23. {primitive-0.1.13 → primitive-0.1.15}/.gitignore +0 -0
  24. {primitive-0.1.13 → primitive-0.1.15}/.vscode/settings.json +0 -0
  25. {primitive-0.1.13 → primitive-0.1.15}/LICENSE.txt +0 -0
  26. {primitive-0.1.13 → primitive-0.1.15}/Makefile +0 -0
  27. {primitive-0.1.13 → primitive-0.1.15}/README.md +0 -0
  28. {primitive-0.1.13 → primitive-0.1.15}/linux setup.md +0 -0
  29. {primitive-0.1.13 → primitive-0.1.15}/pyproject.toml +0 -0
  30. {primitive-0.1.13 → primitive-0.1.15}/requirements.txt +0 -0
  31. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/__init__.py +0 -0
  32. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/agent/commands.py +0 -0
  33. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/auth/__init__.py +0 -0
  34. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/auth/actions.py +0 -0
  35. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/auth/commands.py +0 -0
  36. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/daemons/actions.py +0 -0
  37. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/daemons/commands.py +0 -0
  38. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/files/actions.py +0 -0
  39. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/files/commands.py +0 -0
  40. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/git/__init__.py +0 -0
  41. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/git/actions.py +0 -0
  42. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/git/commands.py +0 -0
  43. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/graphql/__init__.py +0 -0
  44. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/graphql/sdk.py +0 -0
  45. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/hardware/actions.py +0 -0
  46. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/hardware/commands.py +0 -0
  47. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/lint/commands.py +0 -0
  48. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/projects/__init__.py +0 -0
  49. {primitive-0.1.13/src/primitive/simulations → primitive-0.1.15/src/primitive/sim}/__init__.py +0 -0
  50. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/utils/actions.py +0 -0
  51. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/utils/config.py +0 -0
  52. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/utils/files.py +0 -0
  53. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/utils/git.py +0 -0
  54. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/utils/memory_size.py +0 -0
  55. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/utils/printer.py +0 -0
  56. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/utils/shell.py +0 -0
  57. {primitive-0.1.13 → primitive-0.1.15}/src/primitive/utils/verible.py +0 -0
  58. {primitive-0.1.13 → primitive-0.1.15}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: primitive
3
- Version: 0.1.13
3
+ Version: 0.1.15
4
4
  Project-URL: Documentation, https://github.com//primitivecorp/primitive-cli#readme
5
5
  Project-URL: Issues, https://github.com//primitivecorp/primitive-cli/issues
6
6
  Project-URL: Source, https://github.com//primitivecorp/primitive-cli
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2024-present Dylan Stein <dylan@steins.studio>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "0.1.13"
4
+ __version__ = "0.1.15"
@@ -32,7 +32,7 @@ class Agent(BaseAction):
32
32
  sleep(5)
33
33
  continue
34
34
 
35
- job_runs_data = self.primitive.projects.get_job_runs(
35
+ job_runs_data = self.primitive.jobs.get_job_runs(
36
36
  status="pending", first=1, reservation_id=active_reservation_id
37
37
  )
38
38
 
@@ -52,7 +52,7 @@ class Agent(BaseAction):
52
52
  )
53
53
 
54
54
  github_access_token = (
55
- self.primitive.projects.github_access_token_for_job_run(
55
+ self.primitive.jobs.github_access_token_for_job_run(
56
56
  job_run["id"]
57
57
  )
58
58
  )
@@ -65,28 +65,59 @@ class Agent(BaseAction):
65
65
  )
66
66
  )
67
67
 
68
- if job_run["job"]["slug"] == "lint":
69
- logger.debug("Executing Lint Job")
70
-
71
- self.primitive.projects.job_run_update(
72
- job_run["id"], status="request_in_progress"
73
- )
74
-
75
- result, message = self.primitive.lint.execute(
76
- source=downloaded_git_repository_dir
77
- )
78
- if result:
79
- conclusion = "success"
80
- else:
81
- conclusion = "failure"
82
- self.primitive.projects.job_run_update(
83
- job_run["id"],
84
- status="request_completed",
85
- conclusion=conclusion,
86
- stdout=message,
87
- )
88
-
89
- logger.debug("Lint Job Completed")
68
+ match job_run["job"]["slug"]:
69
+ case "lint":
70
+ logger.debug("Executing Lint Job")
71
+
72
+ self.primitive.jobs.job_run_update(
73
+ job_run["id"], status="request_in_progress"
74
+ )
75
+
76
+ result, message = self.primitive.lint.execute(
77
+ source=downloaded_git_repository_dir
78
+ )
79
+ if result:
80
+ conclusion = "success"
81
+ else:
82
+ conclusion = "failure"
83
+ self.primitive.jobs.job_run_update(
84
+ job_run["id"],
85
+ status="request_completed",
86
+ conclusion=conclusion,
87
+ stdout=message,
88
+ )
89
+
90
+ logger.debug("Lint Job Completed")
91
+ case "sim":
92
+ logger.debug("Executing Sim Job")
93
+
94
+ self.primitive.job.job_run_update(
95
+ job_run["id"], status="request_in_progress"
96
+ )
97
+
98
+ result, message = self.primitive.sim.execute(
99
+ source=downloaded_git_repository_dir,
100
+ cmd=(
101
+ "make",
102
+ "all",
103
+ ), # TODO: Change this to use container args container cmd
104
+ )
105
+
106
+ # Attempt artifact collection
107
+ self.primitive.sim.collect_artifacts(
108
+ source=downloaded_git_repository_dir
109
+ )
110
+
111
+ if result:
112
+ conclusion = "success"
113
+ else:
114
+ conclusion = "failure"
115
+ self.primitive.jobs.job_run_update(
116
+ job_run["id"],
117
+ status="request_completed",
118
+ conclusion=conclusion,
119
+ stdout=message,
120
+ )
90
121
 
91
122
  sleep(5)
92
123
  except KeyboardInterrupt:
@@ -10,6 +10,10 @@ from .lint.commands import cli as lint_commands
10
10
  from .agent.commands import cli as agent_commands
11
11
  from .git.commands import cli as git_commands
12
12
  from .daemons.commands import cli as daemons_commands
13
+ from .jobs.commands import cli as jobs_commands
14
+ from .organizations.commands import cli as organizations_commands
15
+ from .projects.commands import cli as projects_commands
16
+ from .sim.commands import cli as sim_commands
13
17
 
14
18
 
15
19
  @click.group()
@@ -61,6 +65,10 @@ cli.add_command(lint_commands, "lint")
61
65
  cli.add_command(agent_commands, "agent")
62
66
  cli.add_command(git_commands, "git")
63
67
  cli.add_command(daemons_commands, "daemons")
68
+ cli.add_command(jobs_commands, "jobs")
69
+ cli.add_command(organizations_commands, "organizations")
70
+ cli.add_command(projects_commands, "projects")
71
+ cli.add_command(sim_commands, "sim")
64
72
 
65
73
  if __name__ == "__main__":
66
74
  cli(obj={})
@@ -4,12 +4,14 @@ from .projects.actions import Projects
4
4
  from .graphql.sdk import create_session
5
5
  from .utils.config import read_config_file
6
6
  from .files.actions import Files
7
- from .simulations.actions import Simulations
7
+ from .sim.actions import Sim
8
8
  from .hardware.actions import Hardware
9
9
  from .lint.actions import Lint
10
10
  from .agent.actions import Agent
11
11
  from .git.actions import Git
12
12
  from .daemons.actions import Daemons
13
+ from .jobs.actions import Jobs
14
+ from .organizations.actions import Organizations
13
15
 
14
16
  from loguru import logger
15
17
 
@@ -55,9 +57,11 @@ class Primitive:
55
57
  )
56
58
 
57
59
  self.auth: Auth = Auth(self)
60
+ self.organizations: Organizations = Organizations(self)
58
61
  self.projects: Projects = Projects(self)
62
+ self.jobs: Jobs = Jobs(self)
59
63
  self.files: Files = Files(self)
60
- self.simulations: Simulations = Simulations(self)
64
+ self.sim: Sim = Sim(self)
61
65
  self.hardware: Hardware = Hardware(self)
62
66
  self.lint: Lint = Lint(self)
63
67
  self.agent: Agent = Agent(self)
@@ -6,6 +6,7 @@ HOME_DIRECTORY = Path.home()
6
6
  CURRENT_USER = str(HOME_DIRECTORY.expanduser()).lstrip("/Users/")
7
7
 
8
8
  PRIMITIVE_BINARY_PATH = Path(HOME_DIRECTORY / ".pyenv" / "shims" / "primitive")
9
+
9
10
  PRIMITIVE_LAUNCH_AGENT_FILEPATH = Path(
10
11
  HOME_DIRECTORY / "Library" / "LaunchAgents" / "tech.primitive.agent.plist"
11
12
  )
@@ -78,6 +79,15 @@ def populate_fresh_launch_agent():
78
79
  PRIMITIVE_LAUNCH_AGENT_FILEPATH.parent.mkdir(parents=True, exist_ok=True)
79
80
  PRIMITIVE_LAUNCH_AGENT_FILEPATH.touch()
80
81
 
82
+ found_primitive_binary_path = PRIMITIVE_BINARY_PATH
83
+ if not PRIMITIVE_BINARY_PATH.exists():
84
+ result = subprocess.run(["which", "primitive"], capture_output=True)
85
+ if result.returncode == 0:
86
+ found_primitive_binary_path = result.stdout.decode().rstrip("\n")
87
+ else:
88
+ print("primitive binary not found")
89
+ return False
90
+
81
91
  PRIMITIVE_LAUNCH_AGENT_FILEPATH.write_text(
82
92
  f"""<?xml version="1.0" encoding="UTF-8"?>
83
93
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -96,7 +106,7 @@ def populate_fresh_launch_agent():
96
106
  </array>
97
107
  <key>ProgramArguments</key>
98
108
  <array>
99
- <string>{PRIMITIVE_BINARY_PATH}</string>
109
+ <string>{found_primitive_binary_path}</string>
100
110
  <string>agent</string>
101
111
  </array>
102
112
  <key>RunAtLoad</key>
@@ -82,8 +82,17 @@ def populate_service_file():
82
82
  "After": "network.target",
83
83
  }
84
84
 
85
+ found_primitive_binary_path = PRIMITIVE_BINARY_PATH
86
+ if not PRIMITIVE_BINARY_PATH.exists():
87
+ result = subprocess.run(["which", "primitive"], capture_output=True)
88
+ if result.returncode == 0:
89
+ found_primitive_binary_path = result.stdout.decode().rstrip("\n")
90
+ else:
91
+ print("primitive binary not found")
92
+ return False
93
+
85
94
  config["Service"] = {
86
- "ExecStart": f"{PRIMITIVE_BINARY_PATH} agent",
95
+ "ExecStart": f"{found_primitive_binary_path} agent",
87
96
  "Restart": "always",
88
97
  "StandardError": f"append:{PRIMITIVE_AGENT_LOGS_FILEPATH}",
89
98
  "StandardOutput": f"append:{PRIMITIVE_AGENT_LOGS_FILEPATH}",
@@ -5,7 +5,88 @@ from gql import gql
5
5
  from primitive.utils.actions import BaseAction
6
6
 
7
7
 
8
- class Projects(BaseAction):
8
+ class Jobs(BaseAction):
9
+ def get_jobs(
10
+ self,
11
+ organization_id: Optional[str] = None,
12
+ project_id: Optional[str] = None,
13
+ job_id: Optional[str] = None,
14
+ slug: Optional[str] = None,
15
+ first: Optional[int] = 1,
16
+ last: Optional[int] = None,
17
+ ):
18
+ query = gql(
19
+ """
20
+ fragment PageInfoFragment on PageInfo {
21
+ hasNextPage
22
+ hasPreviousPage
23
+ startCursor
24
+ endCursor
25
+ }
26
+
27
+ fragment JobFragment on Job {
28
+ id
29
+ pk
30
+ slug
31
+ name
32
+ createdAt
33
+ updatedAt
34
+ }
35
+
36
+ query jobs(
37
+ $before: String
38
+ $after: String
39
+ $first: Int
40
+ $last: Int
41
+ $filters: JobFilters
42
+ ) {
43
+ jobs(
44
+ before: $before
45
+ after: $after
46
+ first: $first
47
+ last: $last
48
+ filters: $filters
49
+ ) {
50
+ totalCount
51
+ pageInfo {
52
+ ...PageInfoFragment
53
+ }
54
+ edges {
55
+ cursor
56
+ node {
57
+ ...JobFragment
58
+ }
59
+ }
60
+ }
61
+ }
62
+ """
63
+ )
64
+
65
+ filters = {}
66
+ if organization_id:
67
+ filters["organization"] = {"id": organization_id}
68
+ if project_id:
69
+ filters["project"] = {"id": project_id}
70
+ if job_id:
71
+ filters["id"] = job_id
72
+ if slug:
73
+ filters["slug"] = {"exact": slug}
74
+
75
+ variables = {
76
+ "first": first,
77
+ "last": last,
78
+ "filters": filters,
79
+ "order": {
80
+ "createdAt": "DESC",
81
+ },
82
+ }
83
+
84
+ result = self.primitive.session.execute(
85
+ query, variable_values=variables, get_execution_result=True
86
+ )
87
+ jobs = [edge["node"] for edge in result.data["jobs"]["edges"]]
88
+ return jobs
89
+
9
90
  def get_job_runs(
10
91
  self,
11
92
  organization_id: Optional[str] = None,
@@ -44,6 +125,10 @@ fragment JobRunFragment on JobRun {
44
125
  name
45
126
  createdAt
46
127
  updatedAt
128
+ }
129
+ jobSettings {
130
+ containerArgs
131
+ containerCommand
47
132
  }
48
133
  gitCommit {
49
134
  sha
@@ -114,12 +199,34 @@ query jobRuns(
114
199
  def get_job_run(self, id: str):
115
200
  query = gql(
116
201
  """
202
+ fragment JobRunFragment on JobRun {
203
+ id
204
+ pk
205
+ createdAt
206
+ updatedAt
207
+ completedAt
208
+ startedAt
209
+ status
210
+ conclusion
211
+ stdout
212
+ job {
213
+ id
214
+ pk
215
+ slug
216
+ name
217
+ createdAt
218
+ updatedAt
219
+ }
220
+ gitCommit {
221
+ sha
222
+ branch
223
+ repoFullName
224
+ }
225
+ }
226
+
117
227
  query jobRun($id: GlobalID!) {
118
228
  jobRun(id: $id) {
119
- id
120
- organization {
121
- id
122
- }
229
+ ...JobRunFragment
123
230
  }
124
231
  }
125
232
  """
@@ -173,3 +280,28 @@ query ghAppTokenForJobRun($jobRunId: GlobalID!) {
173
280
  variables = {"jobRunId": job_run_id}
174
281
  result = self.primitive.session.execute(query, variable_values=variables)
175
282
  return result["ghAppTokenForJobRun"]
283
+
284
+ def get_latest_job_run_for_job(
285
+ self, job_slug: Optional[str] = None, job_id: Optional[str] = None
286
+ ):
287
+ if not job_slug and not job_id:
288
+ raise ValueError("job_slug or job_id is required")
289
+ jobs_results = self.get_jobs(slug=job_slug)
290
+ jobs = [edge["node"] for edge in jobs_results.data["jobs"]["edges"]]
291
+
292
+ job_id = jobs.id
293
+ job_run_results = self.get_job_runs(job_id=job_id, first=1)
294
+ job_run = [edge["node"] for edge in job_run_results.data["job_runs"]["edges"]][
295
+ 0
296
+ ]
297
+ return job_run
298
+
299
+ def job_run_start(
300
+ self,
301
+ job_slug: str,
302
+ job_id: str,
303
+ ):
304
+ if not job_slug and not job_id:
305
+ raise ValueError("job_slug or job_id is required")
306
+
307
+ self.get_jobs(slug=job_slug)
@@ -0,0 +1,41 @@
1
+ import click
2
+
3
+ from ..utils.printer import print_result
4
+
5
+ import typing
6
+
7
+ if typing.TYPE_CHECKING:
8
+ from ..client import Primitive
9
+
10
+
11
+ @click.group()
12
+ @click.pass_context
13
+ def cli(context):
14
+ """Jobs Commands"""
15
+ pass
16
+
17
+
18
+ @cli.command("list")
19
+ @click.pass_context
20
+ def list(
21
+ context,
22
+ organization_slug: str = None,
23
+ organization_id: str = None,
24
+ ):
25
+ """List Job"""
26
+ primitive: Primitive = context.obj.get("PRIMITIVE")
27
+ message = primitive.jobs.get_jobs()
28
+ print_result(message=message, context=context)
29
+
30
+
31
+ @cli.command("details")
32
+ @click.argument("job_slug")
33
+ @click.pass_context
34
+ def details(
35
+ context,
36
+ job_slug: str = None,
37
+ ):
38
+ """List Job"""
39
+ primitive: Primitive = context.obj.get("PRIMITIVE")
40
+ message = primitive.jobs.get_jobs(slug=job_slug)
41
+ print_result(message=message, context=context)
@@ -9,7 +9,7 @@ from ..utils.verible import install_verible
9
9
 
10
10
  class Lint(BaseAction):
11
11
  def execute(self, source: Path = Path.cwd()) -> Tuple[bool, str]:
12
- logger.debug("Starting linter for source: {source}")
12
+ logger.debug(f"Starting linter for source: {source}")
13
13
  files = find_files_for_extension(source, ".sv")
14
14
  if not files:
15
15
  message = "No files found to lint."
@@ -0,0 +1,84 @@
1
+ from typing import Optional
2
+ from gql import gql
3
+
4
+
5
+ from primitive.utils.actions import BaseAction
6
+
7
+
8
+ class Organizations(BaseAction):
9
+ def get_organizations(
10
+ self,
11
+ organization_id: Optional[str] = None,
12
+ slug: Optional[str] = None,
13
+ first: Optional[int] = 1,
14
+ last: Optional[int] = None,
15
+ ):
16
+ query = gql(
17
+ """
18
+ fragment PageInfoFragment on PageInfo {
19
+ hasNextPage
20
+ hasPreviousPage
21
+ startCursor
22
+ endCursor
23
+ }
24
+
25
+ fragment OrganizationFragment on Organization {
26
+ id
27
+ pk
28
+ slug
29
+ name
30
+ createdAt
31
+ updatedAt
32
+ }
33
+
34
+ query organizations(
35
+ $before: String
36
+ $after: String
37
+ $first: Int
38
+ $last: Int
39
+ $filters: OrganizationFilters
40
+ $order: OrganizationOrder
41
+ ) {
42
+ organizations(
43
+ before: $before
44
+ after: $after
45
+ first: $first
46
+ last: $last
47
+ filters: $filters
48
+ order: $order
49
+ ) {
50
+ totalCount
51
+ pageInfo {
52
+ ...PageInfoFragment
53
+ }
54
+ edges {
55
+ cursor
56
+ node {
57
+ ...OrganizationFragment
58
+ }
59
+ }
60
+ }
61
+ }
62
+ """
63
+ )
64
+
65
+ filters = {}
66
+ if organization_id:
67
+ filters["organization"] = {"id": organization_id}
68
+ if slug:
69
+ filters["slug"] = {"exact": slug}
70
+
71
+ variables = {
72
+ "first": first,
73
+ "last": last,
74
+ "filters": filters,
75
+ "order": {
76
+ "createdAt": "DESC",
77
+ },
78
+ }
79
+
80
+ result = self.primitive.session.execute(
81
+ query, variable_values=variables, get_execution_result=True
82
+ )
83
+ organizations = [edge["node"] for edge in result.data["organizations"]["edges"]]
84
+ return organizations
@@ -0,0 +1,24 @@
1
+ import click
2
+
3
+ from ..utils.printer import print_result
4
+
5
+ import typing
6
+
7
+ if typing.TYPE_CHECKING:
8
+ from ..client import Primitive
9
+
10
+
11
+ @click.group()
12
+ @click.pass_context
13
+ def cli(context):
14
+ """Organizations Commands"""
15
+ pass
16
+
17
+
18
+ @cli.command("list")
19
+ @click.pass_context
20
+ def details(context):
21
+ """List Organizations"""
22
+ primitive: Primitive = context.obj.get("PRIMITIVE")
23
+ message = primitive.organizations.get_organizations()
24
+ print_result(message=message, context=context)
@@ -0,0 +1,78 @@
1
+ from gql import gql
2
+
3
+ from typing import Optional
4
+ from primitive.utils.actions import BaseAction
5
+
6
+
7
+ class Projects(BaseAction):
8
+ def get_projects(
9
+ self,
10
+ organization_id: Optional[str] = None,
11
+ slug: Optional[str] = None,
12
+ first: Optional[int] = 1,
13
+ last: Optional[int] = None,
14
+ ):
15
+ query = gql(
16
+ """
17
+ fragment PageInfoFragment on PageInfo {
18
+ hasNextPage
19
+ hasPreviousPage
20
+ startCursor
21
+ endCursor
22
+ }
23
+
24
+ fragment ProjectFragment on Project {
25
+ id
26
+ pk
27
+ slug
28
+ name
29
+ createdAt
30
+ updatedAt
31
+ }
32
+
33
+ query projects(
34
+ $before: String
35
+ $after: String
36
+ $first: Int
37
+ $last: Int
38
+ $filters: ProjectFilters
39
+ ) {
40
+ projects(
41
+ before: $before
42
+ after: $after
43
+ first: $first
44
+ last: $last
45
+ filters: $filters
46
+ ) {
47
+ totalCount
48
+ pageInfo {
49
+ ...PageInfoFragment
50
+ }
51
+ edges {
52
+ cursor
53
+ node {
54
+ ...ProjectFragment
55
+ }
56
+ }
57
+ }
58
+ }
59
+ """
60
+ )
61
+
62
+ filters = {}
63
+ if organization_id:
64
+ filters["organization"] = {"id": organization_id}
65
+ if slug:
66
+ filters["slug"] = {"exact": slug}
67
+
68
+ variables = {
69
+ "first": first,
70
+ "last": last,
71
+ "filters": filters,
72
+ }
73
+
74
+ result = self.primitive.session.execute(
75
+ query, variable_values=variables, get_execution_result=True
76
+ )
77
+ projects = [edge["node"] for edge in result.data["projects"]["edges"]]
78
+ return projects
@@ -0,0 +1,24 @@
1
+ import click
2
+
3
+ from ..utils.printer import print_result
4
+
5
+ import typing
6
+
7
+ if typing.TYPE_CHECKING:
8
+ from ..client import Primitive
9
+
10
+
11
+ @click.group()
12
+ @click.pass_context
13
+ def cli(context):
14
+ """Projects Commands"""
15
+ pass
16
+
17
+
18
+ @cli.command("list")
19
+ @click.pass_context
20
+ def list(context):
21
+ """List Projects"""
22
+ primitive: Primitive = context.obj.get("PRIMITIVE")
23
+ message = primitive.projects.get_projects()
24
+ print_result(message=message, context=context)