feat: automation rules

Owners and admins can set rules of the kind «when a ticket is created,
moves, gets assigned, gets a comment or its pull request merges; if it is
of this type, priority, area or label; then move it, assign it, label it,
change its priority, comment or put it in the active sprint». Rules act as
TaskStream within the workflow, run once per event and trigger no others.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 15:53:31 +02:00
parent 1076205acc
commit 8994590b54
30 changed files with 2543 additions and 4 deletions

View File

@@ -18,6 +18,41 @@ export type AreaOptionInput = {
name: string;
};
/** What a rule does to the ticket */
export type AutomationAction =
| 'ADD_COMMENT'
| 'ADD_LABEL'
| 'ADD_TO_ACTIVE_SPRINT'
| 'ASSIGN'
| 'ASSIGN_REPORTER'
| 'SET_PRIORITY'
| 'SET_STATUS';
export type AutomationActionInput = {
kind: AutomationAction;
value?: string | null | undefined;
};
export type AutomationConditionsInput = {
areas?: Array<string> | null | undefined;
label?: string | null | undefined;
priorities?: Array<IssuePriority> | null | undefined;
types?: Array<string> | null | undefined;
};
/** What starts a rule */
export type AutomationTrigger =
| 'COMMENT_ADDED'
| 'ISSUE_ASSIGNED'
| 'ISSUE_CREATED'
| 'PULL_REQUEST_MERGED'
| 'STATUS_CHANGED';
export type AutomationTriggerInput = {
kind: AutomationTrigger;
status?: string | null | undefined;
};
export type ChangeIssueStatusInput = {
issueId: string;
/** Required by statuses with requiresReason */
@@ -26,6 +61,14 @@ export type ChangeIssueStatusInput = {
status: string;
};
export type CreateAutomationInput = {
action: AutomationActionInput;
conditions?: AutomationConditionsInput | null | undefined;
name: string;
projectId: string;
trigger: AutomationTriggerInput;
};
export type CreateIssueInput = {
/** An area id */
area?: string | null | undefined;
@@ -265,6 +308,104 @@ export type WorkflowStatusInput = {
wipLimit?: number | null | undefined;
};
export type AutomationFieldsFragment = {
id: string;
name: string;
active: boolean;
runs: number;
lastRunAt: string | null;
lastError: string | null;
trigger: { kind: AutomationTrigger; status: string | null };
conditions: {
types: Array<string> | null;
priorities: Array<IssuePriority> | null;
areas: Array<string> | null;
label: string | null;
};
action: { kind: AutomationAction; value: string | null };
createdBy: { id: string; name: string };
};
export type AutomationsQueryVariables = Exact<{
projectId: string;
}>;
export type AutomationsQuery = {
automations: Array<{
id: string;
name: string;
active: boolean;
runs: number;
lastRunAt: string | null;
lastError: string | null;
trigger: { kind: AutomationTrigger; status: string | null };
conditions: {
types: Array<string> | null;
priorities: Array<IssuePriority> | null;
areas: Array<string> | null;
label: string | null;
};
action: { kind: AutomationAction; value: string | null };
createdBy: { id: string; name: string };
}>;
};
export type CreateAutomationMutationVariables = Exact<{
input: CreateAutomationInput;
}>;
export type CreateAutomationMutation = {
createAutomation: {
id: string;
name: string;
active: boolean;
runs: number;
lastRunAt: string | null;
lastError: string | null;
trigger: { kind: AutomationTrigger; status: string | null };
conditions: {
types: Array<string> | null;
priorities: Array<IssuePriority> | null;
areas: Array<string> | null;
label: string | null;
};
action: { kind: AutomationAction; value: string | null };
createdBy: { id: string; name: string };
};
};
export type UpdateAutomationMutationVariables = Exact<{
automationId: string;
name?: string | null | undefined;
active?: boolean | null | undefined;
}>;
export type UpdateAutomationMutation = {
updateAutomation: {
id: string;
name: string;
active: boolean;
runs: number;
lastRunAt: string | null;
lastError: string | null;
trigger: { kind: AutomationTrigger; status: string | null };
conditions: {
types: Array<string> | null;
priorities: Array<IssuePriority> | null;
areas: Array<string> | null;
label: string | null;
};
action: { kind: AutomationAction; value: string | null };
createdBy: { id: string; name: string };
};
};
export type DeleteAutomationMutationVariables = Exact<{
automationId: string;
}>;
export type DeleteAutomationMutation = { deleteAutomation: boolean };
export type UserRefFieldsFragment = { id: string; name: string };
export type ProjectFieldsFragment = {
@@ -1790,6 +1931,93 @@ export const UserRefFieldsFragmentDoc = {
},
],
} as unknown as DocumentNode<UserRefFieldsFragment, unknown>;
export const AutomationFieldsFragmentDoc = {
kind: 'Document',
definitions: [
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'AutomationFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'Automation' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
{ kind: 'Field', name: { kind: 'Name', value: 'active' } },
{ kind: 'Field', name: { kind: 'Name', value: 'runs' } },
{ kind: 'Field', name: { kind: 'Name', value: 'lastRunAt' } },
{ kind: 'Field', name: { kind: 'Name', value: 'lastError' } },
{
kind: 'Field',
name: { kind: 'Name', value: 'trigger' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'kind' } },
{ kind: 'Field', name: { kind: 'Name', value: 'status' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'conditions' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'types' } },
{ kind: 'Field', name: { kind: 'Name', value: 'priorities' } },
{ kind: 'Field', name: { kind: 'Name', value: 'areas' } },
{ kind: 'Field', name: { kind: 'Name', value: 'label' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'action' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'kind' } },
{ kind: 'Field', name: { kind: 'Name', value: 'value' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'createdBy' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: { kind: 'Name', value: 'UserRefFields' },
},
],
},
},
],
},
},
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'UserRefFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'UserRef' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
],
},
},
],
} as unknown as DocumentNode<AutomationFieldsFragment, unknown>;
export const ProjectFieldsFragmentDoc = {
kind: 'Document',
definitions: [
@@ -2703,6 +2931,488 @@ export const WebhookFieldsFragmentDoc = {
},
],
} as unknown as DocumentNode<WebhookFieldsFragment, unknown>;
export const AutomationsDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'query',
name: { kind: 'Name', value: 'Automations' },
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'projectId' },
},
type: {
kind: 'NonNullType',
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'automations' },
arguments: [
{
kind: 'Argument',
name: { kind: 'Name', value: 'projectId' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'projectId' },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: { kind: 'Name', value: 'AutomationFields' },
},
],
},
},
],
},
},
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'UserRefFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'UserRef' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
],
},
},
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'AutomationFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'Automation' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
{ kind: 'Field', name: { kind: 'Name', value: 'active' } },
{ kind: 'Field', name: { kind: 'Name', value: 'runs' } },
{ kind: 'Field', name: { kind: 'Name', value: 'lastRunAt' } },
{ kind: 'Field', name: { kind: 'Name', value: 'lastError' } },
{
kind: 'Field',
name: { kind: 'Name', value: 'trigger' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'kind' } },
{ kind: 'Field', name: { kind: 'Name', value: 'status' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'conditions' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'types' } },
{ kind: 'Field', name: { kind: 'Name', value: 'priorities' } },
{ kind: 'Field', name: { kind: 'Name', value: 'areas' } },
{ kind: 'Field', name: { kind: 'Name', value: 'label' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'action' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'kind' } },
{ kind: 'Field', name: { kind: 'Name', value: 'value' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'createdBy' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: { kind: 'Name', value: 'UserRefFields' },
},
],
},
},
],
},
},
],
} as unknown as DocumentNode<AutomationsQuery, AutomationsQueryVariables>;
export const CreateAutomationDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'mutation',
name: { kind: 'Name', value: 'CreateAutomation' },
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'input' },
},
type: {
kind: 'NonNullType',
type: {
kind: 'NamedType',
name: { kind: 'Name', value: 'CreateAutomationInput' },
},
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'createAutomation' },
arguments: [
{
kind: 'Argument',
name: { kind: 'Name', value: 'input' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'input' },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: { kind: 'Name', value: 'AutomationFields' },
},
],
},
},
],
},
},
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'UserRefFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'UserRef' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
],
},
},
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'AutomationFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'Automation' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
{ kind: 'Field', name: { kind: 'Name', value: 'active' } },
{ kind: 'Field', name: { kind: 'Name', value: 'runs' } },
{ kind: 'Field', name: { kind: 'Name', value: 'lastRunAt' } },
{ kind: 'Field', name: { kind: 'Name', value: 'lastError' } },
{
kind: 'Field',
name: { kind: 'Name', value: 'trigger' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'kind' } },
{ kind: 'Field', name: { kind: 'Name', value: 'status' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'conditions' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'types' } },
{ kind: 'Field', name: { kind: 'Name', value: 'priorities' } },
{ kind: 'Field', name: { kind: 'Name', value: 'areas' } },
{ kind: 'Field', name: { kind: 'Name', value: 'label' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'action' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'kind' } },
{ kind: 'Field', name: { kind: 'Name', value: 'value' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'createdBy' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: { kind: 'Name', value: 'UserRefFields' },
},
],
},
},
],
},
},
],
} as unknown as DocumentNode<
CreateAutomationMutation,
CreateAutomationMutationVariables
>;
export const UpdateAutomationDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'mutation',
name: { kind: 'Name', value: 'UpdateAutomation' },
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'automationId' },
},
type: {
kind: 'NonNullType',
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
},
},
{
kind: 'VariableDefinition',
variable: { kind: 'Variable', name: { kind: 'Name', value: 'name' } },
type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } },
},
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'active' },
},
type: { kind: 'NamedType', name: { kind: 'Name', value: 'Boolean' } },
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'updateAutomation' },
arguments: [
{
kind: 'Argument',
name: { kind: 'Name', value: 'automationId' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'automationId' },
},
},
{
kind: 'Argument',
name: { kind: 'Name', value: 'name' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'name' },
},
},
{
kind: 'Argument',
name: { kind: 'Name', value: 'active' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'active' },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: { kind: 'Name', value: 'AutomationFields' },
},
],
},
},
],
},
},
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'UserRefFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'UserRef' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
],
},
},
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'AutomationFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'Automation' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
{ kind: 'Field', name: { kind: 'Name', value: 'active' } },
{ kind: 'Field', name: { kind: 'Name', value: 'runs' } },
{ kind: 'Field', name: { kind: 'Name', value: 'lastRunAt' } },
{ kind: 'Field', name: { kind: 'Name', value: 'lastError' } },
{
kind: 'Field',
name: { kind: 'Name', value: 'trigger' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'kind' } },
{ kind: 'Field', name: { kind: 'Name', value: 'status' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'conditions' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'types' } },
{ kind: 'Field', name: { kind: 'Name', value: 'priorities' } },
{ kind: 'Field', name: { kind: 'Name', value: 'areas' } },
{ kind: 'Field', name: { kind: 'Name', value: 'label' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'action' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'kind' } },
{ kind: 'Field', name: { kind: 'Name', value: 'value' } },
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'createdBy' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: { kind: 'Name', value: 'UserRefFields' },
},
],
},
},
],
},
},
],
} as unknown as DocumentNode<
UpdateAutomationMutation,
UpdateAutomationMutationVariables
>;
export const DeleteAutomationDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'mutation',
name: { kind: 'Name', value: 'DeleteAutomation' },
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'automationId' },
},
type: {
kind: 'NonNullType',
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'deleteAutomation' },
arguments: [
{
kind: 'Argument',
name: { kind: 'Name', value: 'automationId' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'automationId' },
},
},
],
},
],
},
},
],
} as unknown as DocumentNode<
DeleteAutomationMutation,
DeleteAutomationMutationVariables
>;
export const GitIntegrationDocument = {
kind: 'Document',
definitions: [