From ad3ca0a97b80d25da4ccef7e74c85ad1dcd7dd9e Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Wed, 10 Jul 2024 16:22:45 +1000 Subject: [PATCH 001/369] Initial restructuring of docs. --- data/nav.yml | 149 ++++++++++++++++++++++-------------------- data/tiles.yml | 22 ------- pages/integrations.md | 2 +- pages/platform.md | 5 ++ 4 files changed, 84 insertions(+), 94 deletions(-) create mode 100644 pages/platform.md diff --git a/data/nav.yml b/data/nav.yml index b32c6ba474..f810f9f55c 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -232,14 +232,6 @@ path: "agent/v2/cli-artifact" - name: "pipeline" path: "agent/v2/cli-pipeline" - - name: "CLI" - children: - - name: "Overview" - path: "cli" - - name: "Installation" - path: "cli/installation" - - name: "Configuration" - path: "cli/configuration" - name: "Configure pipelines" children: - name: "Overview" @@ -358,16 +350,6 @@ path: "pipelines/security/oidc" - name: "OIDC with AWS" path: "pipelines/security/oidc/aws" - - name: "Team management" - children: - - name: "Overview" - path: "team-management" - - name: "User and team permissions" - path: "team-management/permissions" - - name: "Enforce 2FA" - path: "team-management/enforce-2fa" - - name: "System banners" - path: "team-management/system-banners" - name: "Governance" children: - name: "Overview" @@ -391,6 +373,38 @@ - name: "Cluster queue metrics" path: "pipelines/cluster-queue-metrics" pill: "beta" + - name: "Integrations" + children: + - name: "Overview" + path: "integrations" + - name: "Plugins" + children: + - name: "Overview" + path: "plugins" + - name: "Using plugins" + path: "plugins/using" + - name: "Plugins directory" + path: "plugins/directory" + - name: "Plugin tools" + path: "plugins/tools" + - name: "Writing plugins" + path: "plugins/writing" + - name: "Other integrations" + children: + - name: "Amazon EventBridge" + path: "integrations/amazon-eventbridge" + - name: "Artifactory" + path: "integrations/artifactory" + - name: "Build status badges" + path: "integrations/build-status-badges" + - name: "CCMenu & CCTray" + path: "integrations/cc-menu" + - name: "Docker Hub" + path: "integrations/docker-hub" + - name: "PagerDuty" + path: "integrations/pagerduty" + - name: "Slack" + path: "integrations/slack" - name: "Test Analytics" path: "test-analytics" children: @@ -502,6 +516,52 @@ path: "packages/ruby" - name: "Terraform" path: "packages/terraform" +- name: "Platform" + path: "platform" + children: + - name: "Overview" + path: "platform" + - name: "Team management" + start_expanded: true + children: + - name: "Overview" + path: "team-management" + - name: "User and team permissions" + path: "team-management/permissions" + - name: "Enforce 2FA" + path: "team-management/enforce-2fa" + - name: "System banners" + path: "team-management/system-banners" + - name: "CLI" + children: + - name: "Overview" + path: "cli" + - name: "Installation" + path: "cli/installation" + - name: "Configuration" + path: "cli/configuration" + - name: "SSO" + children: + - name: "Overview" + path: "integrations/sso" + - name: "Okta" + path: "integrations/sso/okta" + - name: "ADFS" + path: "integrations/sso/adfs" + - name: "Google Workspace" + path: "integrations/sso/google-workspace" + - name: "Google Workspace (SAML)" + path: "integrations/sso/google-workspace-saml" + - name: "GitHub" + path: "integrations/sso/github-sso" + - name: "OneLogin" + path: "integrations/sso/onelogin" + - name: "Azure AD" + path: "integrations/sso/azure-ad" + - name: "Custom SAML" + path: "integrations/sso/custom-saml" + - name: "Set up with GraphQL" + path: "integrations/sso/sso-setup-with-graphql" - name: "APIs" path: "apis" children: @@ -595,56 +655,3 @@ path: "apis/webhooks/ping-events" - name: "Integrations" path: "apis/webhooks/integrations" -- name: "Integrations" - path: "integrations" - children: - - name: "Overview" - path: "integrations" - - name: "Plugins" - children: - - name: "Overview" - path: "plugins" - - name: "Using plugins" - path: "plugins/using" - - name: "Plugins directory" - path: "plugins/directory" - - name: "Plugin tools" - path: "plugins/tools" - - name: "Writing plugins" - path: "plugins/writing" - - name: "SSO" - children: - - name: "Overview" - path: "integrations/sso" - - name: "Okta" - path: "integrations/sso/okta" - - name: "ADFS" - path: "integrations/sso/adfs" - - name: "Google Workspace" - path: "integrations/sso/google-workspace" - - name: "Google Workspace (SAML)" - path: "integrations/sso/google-workspace-saml" - - name: "GitHub" - path: "integrations/sso/github-sso" - - name: "OneLogin" - path: "integrations/sso/onelogin" - - name: "Azure AD" - path: "integrations/sso/azure-ad" - - name: "Custom SAML" - path: "integrations/sso/custom-saml" - - name: "Set up with GraphQL" - path: "integrations/sso/sso-setup-with-graphql" - - name: "Amazon EventBridge" - path: "integrations/amazon-eventbridge" - - name: "Artifactory" - path: "integrations/artifactory" - - name: "Build status badges" - path: "integrations/build-status-badges" - - name: "CCMenu & CCTray" - path: "integrations/cc-menu" - - name: "Docker Hub" - path: "integrations/docker-hub" - - name: "PagerDuty" - path: "integrations/pagerduty" - - name: "Slack" - path: "integrations/slack" diff --git a/data/tiles.yml b/data/tiles.yml index f120f99eb3..e00abeaba1 100644 --- a/data/tiles.yml +++ b/data/tiles.yml @@ -57,28 +57,6 @@ integrations: url: "/docs/plugins/tools" - text: "Writing Plugins" url: "/docs/plugins/writing" - - title: "SSO" - desc: "You can use a single sign-on (SSO) provider to protect access to your organization's data in Buildkite. Buildkite supports many different SSO providers." - url: "/docs/integrations/sso" - links: - - text: "Okta" - url: "/docs/integrations/sso/okta" - - text: "ADFS" - url: "/docs/integrations/sso/adfs" - - text: "Google Workspace" - url: "/docs/integrations/sso/google-workspace" - - text: "Google Workspace (SAML)" - url: "/docs/integrations/sso/google-workspace-saml" - - text: "GitHub" - url: "/docs/integrations/sso/github-sso" - - text: "OneLogin" - url: "/docs/integrations/sso/onelogin" - - text: "Azure AD" - url: "/docs/integrations/sso/azure-ad" - - text: "Custom SAML" - url: "/docs/integrations/sso/custom-saml" - - text: "Setup with GraphQL" - url: "/docs/integrations/sso/sso-setup-with-graphql" - title: "Other integrations" desc: "Buildkite offers other integrations to improve your workflow." links: diff --git a/pages/integrations.md b/pages/integrations.md index 102346ae5b..82a810f7d5 100644 --- a/pages/integrations.md +++ b/pages/integrations.md @@ -6,4 +6,4 @@ template: "landing_page" <%= tiles "integrations" %> -If you're looking for [Source control integrations](/docs/integrations/source-control) for Pipelines, they are over in the [Pipelines](/docs/integrations/source-control) nav. +Learn more about source control integrations in [Connect source control](/docs/integrations/source-control). diff --git a/pages/platform.md b/pages/platform.md new file mode 100644 index 0000000000..80c7b2179d --- /dev/null +++ b/pages/platform.md @@ -0,0 +1,5 @@ +--- +template: "landing_page" +--- + +# The Buildkite platform From 1803ade02bff02d32efe3e047b7c1833c734112e Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 11 Jul 2024 09:29:26 +1000 Subject: [PATCH 002/369] Add 'permissions' page back to Pipelines docs. --- config/routes.rb | 3 +- data/nav.yml | 6 +- pages/packages/security.md | 7 +- pages/packages/{ => security}/permissions.md | 0 pages/pipelines/security/permissions.md | 82 ++++++++++++++++++++ pages/team_management/permissions.md | 59 +------------- 6 files changed, 96 insertions(+), 61 deletions(-) rename pages/packages/{ => security}/permissions.md (100%) create mode 100644 pages/pipelines/security/permissions.md diff --git a/config/routes.rb b/config/routes.rb index 1d262438e2..6bd7b81222 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -64,12 +64,13 @@ get "/docs/integrations/sso/g-cloud-identity", to: redirect("/docs/integrations/sso/google-workspace-saml") get "/docs/packages/manage-repositories", to: redirect("/docs/packages/manage-registries") get "/docs/packages/nodejs", to: redirect("/docs/packages/javascript") + get "/docs/packages/permissions", to: redirect("/docs/packages/security/permissions") get "/docs/pipelines/emoji", to: redirect("/docs/pipelines/emojis") get "/docs/pipelines/images-in-log-output", to: redirect("/docs/pipelines/links-and-images-in-log-output") get "/docs/pipelines/pipelines", to: redirect("/docs/pipelines") get "/docs/pipelines/ignoring-a-commit", to: redirect("/docs/pipelines/skipping#ignore-a-commit") get "/docs/pipelines/parallel-builds", to: redirect("/docs/tutorials/parallel-builds") - get "/docs/pipelines/permissions", to: redirect("/docs/team-management/permissions") + get "/docs/pipelines/permissions", to: redirect("/docs/pipelines/security/permissions") get "/docs/pipelines/plugins", to: redirect("/docs/plugins") get "/docs/pipelines/uploading-pipelines", to: redirect("/docs/pipelines/defining-steps") get "/docs/pipelines/security-overview", to: redirect("/docs/pipelines/security/overview") diff --git a/data/nav.yml b/data/nav.yml index f810f9f55c..a218327c89 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -350,6 +350,8 @@ path: "pipelines/security/oidc" - name: "OIDC with AWS" path: "pipelines/security/oidc/aws" + - name: "Permissions" + path: "pipelines/security/permissions" - name: "Governance" children: - name: "Overview" @@ -488,8 +490,8 @@ path: "packages/security" - name: "OIDC" path: "packages/security/oidc" - - name: "Permissions" - path: "packages/permissions" + - name: "Permissions" + path: "packages/security/permissions" - name: "Package ecosystems" start_expanded: true children: diff --git a/pages/packages/security.md b/pages/packages/security.md index 91cfd29a30..05c96b27ca 100644 --- a/pages/packages/security.md +++ b/pages/packages/security.md @@ -4,6 +4,9 @@ toc: false # Security -Customer security is paramount to Buildkite. Buildkite Packages provides mechanisms to restrict access to your registries from Buildkite Agents and their pipeline's jobs, as well as other 3rd party systems that implement OIDC-compatible policies. +Customer security is paramount to Buildkite. Buildkite Packages provides mechanisms to restrict access to your registries from Buildkite Agents and their pipeline's jobs, as well as other 3rd party systems that implement OIDC-compatible policies, as well as how to manage access to Buildkite Packages. -Learn more about OIDC tokens in [OIDC with Buildkite Packages](/docs/packages/security/oidc). +This section contains the following topics: + +- [OIDC with Buildkite Packages](/docs/packages/security/oidc) and how to restrict access registries through OIDC tokens. +- [User, team, and registry permissions](/docs/packages/security/permissions) and how to manage team and user access to registries. diff --git a/pages/packages/permissions.md b/pages/packages/security/permissions.md similarity index 100% rename from pages/packages/permissions.md rename to pages/packages/security/permissions.md diff --git a/pages/pipelines/security/permissions.md b/pages/pipelines/security/permissions.md new file mode 100644 index 0000000000..9e51a3bc75 --- /dev/null +++ b/pages/pipelines/security/permissions.md @@ -0,0 +1,82 @@ +# User, team, and pipeline permissions + +Customers on the Buildkite [Pro and Enterprise](https://buildkite.com/pricing) plans can manage registry permissions using the [_teams_ feature](#manage-teams-and-permissions). This feature allows you to apply access permissions and functionality controls for one or more groups of users (that is, _teams_) on each pipeline throughout your organization. + +Enterprise customers can configure pipeline permissions for all users across their Buildkite organization through the **Security** page. Learn more about this feature in [Manage organization security for pipelines](#manage-organization-security-for-pipelines). + +## Manage teams and permissions + +To manage teams across the Buildkite Packages application, a _Buildkite organization administrator_ first needs to enable this feature across their organization. Learn more about how to do this in the [Manage teams and permissions section of Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions). + +Once the _teams_ feature is enabled, you can see the teams that you're a member of from the **Users** page, which: + +- As a Buildkite organization administrator, you can access by selecting **Settings** in the global navigation > [**Users**](https://buildkite.com/organizations/~/users/). + +- As any other user, you can access by selecting **Teams** in the global navigation > [**Users**](https://buildkite.com/organizations/~/users/). + +### Organization-level permissions + +Learn more about what a _Buildkite organization administrator_ can do in the [Organization-level permissions section of the Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions-organization-level-permissions). + +As an organization administrator, you can access the [**Organization Settings** page](https://buildkite.com/organizations/~/settings) by selecting **Settings** in the global navigation, where you can do the following: + +- Add new teams or edit existing ones in the [**Team** section](https://buildkite.com/organizations/~/teams). + +- After selecting a team, you can view and administer the member-, [pipeline-](/docs/team-management/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions), [registry-](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) and [team-](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions)level settings for that team. + +**Note:** Registry-level settings are only available once [Buildkite Packages has been enabled](/docs/packages/permissions#enabling-buildkite-packages). + +### Team-level permissions + +Learn more about what _team members_ are and what _team maintainers_ can do in the [Team-level permissions section of the Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions). + +### Pipeline-level permissions + +When the [teams feature is enabled](#manage-teams-and-permissions), any user can create a new pipeline, as long as this user is a member of at least one team within the Buildkite organization, and this team has the **Create pipelines** [team member permission](#manage-teams-and-permissions-team-level-permissions). + +When you create a new pipeline in Buildkite: + +- You are automatically granted the **Full Access** (`MANAGE_BUILD_AND_READ`) permission to this pipeline. +- Any members of teams to which you provide access to this pipeline are also granted the **Full Access** permission. + +**Full Access** on a pipeline allows you to: + +- View and create builds or rebuilds. +- Edit pipeline settings, which includes the ability to change the pipeline's visibility. +- Archive the pipeline or delete the pipeline. +- Provide access to other users, by adding the pipeline to other teams that you are a [team maintainer](#manage-teams-and-permissions-team-level-permissions) on. + +Any user with the **Full Access** permission on a pipeline can change its permission to either: + +- **Build & Read** (`BUILD_AND_READ`), which allows you to view and create builds or rebuilds, but _not_: + * Edit the pipeline settings. + * Archive or delete the pipeline. + * Provide access to other users. +- **Read Only** (`READ_ONLY`), which allows you to view builds only, but _not_: + * Create builds or issue rebuilds. + * Edit the pipeline settings. + * Archive or delete the pipeline. + * Provide access to other users. + +A user who is a member of at least one team with **Full Access** permission to a pipeline can change the permission on this pipeline. However, once this user loses **Full Access** through their last team with this permission on this pipeline, the user then loses the ability to change the pipeline's permissions in any team they are a member of. + +Another user with **Full Access** to this pipeline or a [Buildkite organization administrator](#manage-teams-and-permissions-organization-level-permissions) is required to change the pipeline's permission back to **Full Access** again. + +## Manage organization security for pipelines + +Enterprise customers can configure pipeline action permissions and related security features for all users across their Buildkite organization. These features can be used either with or without the [teams feature enabled](#manage-teams-and-permissions). + +These user-level permissions and security features are managed by _Buildkite organization administrators_. To access this feature: + +1. Select **Settings** in the global navigation to access the [**Organization Settings**](https://buildkite.com/organizations/~/settings) page. + +1. Select [**Security** > **Pipelines** tab](https://buildkite.com/organizations/~/security/pipelines) to access your organization's security for pipelines page. + +From this page, you can configure the following permissions for all users across your Buildkite organization: + +- **Create Pipelines**—if the [teams feature](#manage-teams-and-permissions) is enabled, then this permission is controlled at a [team-level](#manage-teams-and-permissions-team-level-permissions) and therefore, this option will be unavailable on this page. +- **Delete pipelines** +- **Change Pipeline Visibility**—Make private pipelines publicly available. +- **Change Notification Services**—Allows notification services to be created, edited, and deleted. +- **Manage Agent Registration Tokens**—Allows [agent tokens](/docs/agent/v3/tokens) to be created, edited, and deleted. +- **Stop Agents**—Allows users to disconnect agents from Buildkite. diff --git a/pages/team_management/permissions.md b/pages/team_management/permissions.md index 7e64b916e7..b52d61c76a 100644 --- a/pages/team_management/permissions.md +++ b/pages/team_management/permissions.md @@ -2,8 +2,6 @@ Customers on the Buildkite [Pro and Enterprise](https://buildkite.com/pricing) plans can manage permissions using the _teams_ feature. Learn more about this feature in [Manage teams and permissions](#manage-teams-and-permissions). -Enterprise customers can configure pipeline permissions and security features for all users across their Buildkite organization through the **Security** page. Learn more about this feature in [Manage organization security for pipelines](#manage-organization-security-for-pipelines). - ## Manage teams and permissions The _teams_ feature allows you to apply access permissions and functionality controls for one or more groups of users (that is, _teams_) on each pipeline, test suite, registry, or any combination of these, throughout your organization. @@ -33,7 +31,7 @@ A user who is a _Buildkite organization administrator_ can access the [**Organiz - From the **Teams** page: * Create a new team, using the **New Team** button. - * Administer (with full control) the [team-](#manage-teams-and-permissions-team-level-permissions), [pipeline-](#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions) and [registry-](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions)level settings throughout their Buildkite organization. + * Administer (with full control) the [team-](#manage-teams-and-permissions-team-level-permissions), [pipeline-](/docs/pipelines/security/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions) and [registry-](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions)level settings throughout their Buildkite organization. **Note:** Registry-level settings are only available once [Buildkite Packages has been enabled](/docs/packages/permissions#enabling-buildkite-packages). @@ -59,7 +57,7 @@ A user who is a _team maintainer_ on an existing team can: * Remove a user from this team, by selecting the user's **Remove** button. * Change the permission for all users in this team on any: - - [pipeline](#manage-teams-and-permissions-pipeline-level-permissions) in the team to **Full Access**, **Build & Read** or **Read Only**. + - [pipeline](/docs/pipelines/security/permissions#manage-teams-and-permissions-pipeline-level-permissions) in the team to **Full Access**, **Build & Read** or **Read Only**. - [test suite](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions) in the team to **Full Access** or **Read Only**. - [registry](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) in the team to **Full Access**, **Read & Write** or **Read Only**. @@ -86,44 +84,12 @@ A user who is a _team maintainer_ on an existing team can: As indicated in the Buildkite interface, a user who is in a team is known as a **Team Member**, and such users have fewer permissions within the team (that is, no team management capabilities) than a **Team Maintainer**. -All team members in a team have the same level of access to the [pipelines](#manage-teams-and-permissions-pipeline-level-permissions), [test suites](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions), and [registries](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) in the team. If you need to have more fine grained control over the pipelines, test suites or registries in a team, you can create more teams with different permissions. +All team members in a team have the same level of access to the [pipelines](/docs/pipelines/security/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suites](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions), and [registries](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) in the team. If you need to have more fine grained control over the pipelines, test suites or registries in a team, you can create more teams with different permissions. > 🚧 Changing **Full Access** permissions on pipelines, test suites and registries > As a team maintainer, once you change the permission on any of these items away from **Full Access**, you could lose the ability to change the permissions on that item again. This can happen if you are no longer a member of a team that provides **Full Access** to this item. > A [Buildkite organization administrator](#manage-teams-and-permissions-organization-level-permissions) is required to change any item's permissions back to **Full Access** again. -### Pipeline-level permissions - -When the [teams feature is enabled](#manage-teams-and-permissions), any user can create a new pipeline, as long as this user is a member of at least one team within the Buildkite organization, and this team has the **Create pipelines** [team member permission](#manage-teams-and-permissions-team-level-permissions). - -When you create a new pipeline in Buildkite: - -- You are automatically granted the **Full Access** (`MANAGE_BUILD_AND_READ`) permission to this pipeline. -- Any members of teams to which you provide access to this pipeline are also granted the **Full Access** permission. - -**Full Access** on a pipeline allows you to: - -- View and create builds or rebuilds. -- Edit pipeline settings, which includes the ability to change the pipeline's visibility. -- Archive the pipeline or delete the pipeline. -- Provide access to other users, by adding the pipeline to other teams that you are a [team maintainer](#manage-teams-and-permissions-team-level-permissions) on. - -Any user with the **Full Access** permission on a pipeline can change its permission to either: - -- **Build & Read** (`BUILD_AND_READ`), which allows you to view and create builds or rebuilds, but _not_: - * Edit the pipeline settings. - * Archive or delete the pipeline. - * Provide access to other users. -- **Read Only** (`READ_ONLY`), which allows you to view builds only, but _not_: - * Create builds or issue rebuilds. - * Edit the pipeline settings. - * Archive or delete the pipeline. - * Provide access to other users. - -A user who is a member of at least one team with **Full Access** permission to a pipeline can change the permission on this pipeline. However, once this user loses **Full Access** through their last team with this permission on this pipeline, the user then loses the ability to change the pipeline's permissions in any team they are a member of. - -Another user with **Full Access** to this pipeline or a [Buildkite organization administrator](#manage-teams-and-permissions-organization-level-permissions) is required to change the pipeline's permission back to **Full Access** again. - ### Programmatically managing teams You can programmatically manage your teams using our GraphQL API. @@ -207,25 +173,6 @@ mutation RemoveOrganizationMember { } ``` -## Manage organization security for pipelines - -Enterprise customers can configure pipeline action permissions and related security features for all users across their Buildkite organization. These features can be used either with or without the [teams feature enabled](#manage-teams-and-permissions). - -These user-level permissions and security features are managed by _Buildkite organization administrators_. To access this feature: - -1. Select **Settings** in the global navigation to access the [**Organization Settings**](https://buildkite.com/organizations/~/settings) page. - -1. Select [**Security** > **Pipelines** tab](https://buildkite.com/organizations/~/security/pipelines) to access your organization's security for pipelines page. - -From this page, you can configure the following permissions for all users across your Buildkite organization: - -- **Create Pipelines**—if the [teams feature](#manage-teams-and-permissions) is enabled, then this permission is controlled at a [team-level](#manage-teams-and-permissions-team-level-permissions) and therefore, this option will be unavailable on this page. -- **Delete pipelines** -- **Change Pipeline Visibility**—Make private pipelines publicly available. -- **Change Notification Services**—Allows notification services to be created, edited, and deleted. -- **Manage Agent Registration Tokens**—Allows [agent tokens](/docs/agent/v3/tokens) to be created, edited, and deleted. -- **Stop Agents**—Allows users to disconnect agents from Buildkite. - ## Removing users during a security incident If you believe that a user account has been compromised, the recommended incident response is to remove such a user from your Buildkite organization immediately. This will entirely remove their ability to impact your organization and protect you from any further actions that the user could take. From 829e96e3f1d766cb53bb20be7d10e2bb1a5a8e57 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 11 Jul 2024 09:52:31 +1000 Subject: [PATCH 003/369] Fix broken links resulting from shifting pages around. --- pages/apis/rest_api/agents.md | 4 ++-- pages/packages/security/permissions.md | 2 +- pages/pipelines/security/permissions.md | 2 +- pages/test_analytics/permissions.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pages/apis/rest_api/agents.md b/pages/apis/rest_api/agents.md index d371ce8786..77b30424a8 100644 --- a/pages/apis/rest_api/agents.md +++ b/pages/apis/rest_api/agents.md @@ -142,8 +142,8 @@ Success response: `200 OK` > 📘 Required permissions > To stop an agent you need either -- An Admin user API token with `write_agents` scope -- Or, if you're using the Buildkite organization's security for pipelines feature, a user token with the Stop Agents permission. +- An Admin user API token with `write_agents` [scope](/docs/apis/managing-api-tokens#token-scopes) +- Or, if you're using the Buildkite organization's [security for pipelines](/docs/pipelines/security/permissions#manage-organization-security-for-pipelines) feature, a user token with the Stop Agents permission. Instruct an agent to stop accepting new build jobs and shut itself down. diff --git a/pages/packages/security/permissions.md b/pages/packages/security/permissions.md index 2033f63852..f58ffe12eb 100644 --- a/pages/packages/security/permissions.md +++ b/pages/packages/security/permissions.md @@ -22,7 +22,7 @@ As an organization administrator, you can access the [**Organization Settings** - Add new teams or edit existing ones in the [**Team** section](https://buildkite.com/organizations/~/teams). - * After selecting a team, you can view and administer the member-, [pipeline-](/docs/team-management/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions), [registry-](#manage-teams-and-permissions-registry-level-permissions) and [team-](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions)level settings for that team. + * After selecting a team, you can view and administer the member-, [pipeline-](/docs/pipelines/security/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions), [registry-](#manage-teams-and-permissions-registry-level-permissions) and [team-](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions)level settings for that team. - [Enable Buildkite Packages](#enabling-buildkite-packages) for your Buildkite organization. diff --git a/pages/pipelines/security/permissions.md b/pages/pipelines/security/permissions.md index 9e51a3bc75..a0687bc63a 100644 --- a/pages/pipelines/security/permissions.md +++ b/pages/pipelines/security/permissions.md @@ -22,7 +22,7 @@ As an organization administrator, you can access the [**Organization Settings** - Add new teams or edit existing ones in the [**Team** section](https://buildkite.com/organizations/~/teams). -- After selecting a team, you can view and administer the member-, [pipeline-](/docs/team-management/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions), [registry-](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) and [team-](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions)level settings for that team. +- After selecting a team, you can view and administer the member-, [pipeline-](#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions), [registry-](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) and [team-](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions)level settings for that team. **Note:** Registry-level settings are only available once [Buildkite Packages has been enabled](/docs/packages/permissions#enabling-buildkite-packages). diff --git a/pages/test_analytics/permissions.md b/pages/test_analytics/permissions.md index 96c348c569..dc202a6cfd 100644 --- a/pages/test_analytics/permissions.md +++ b/pages/test_analytics/permissions.md @@ -26,7 +26,7 @@ As an organization administrator, you can access the [**Organization Settings** <%= image "team-section-list.png", alt: "Screenshot of the Team section, showing a list of Teams" %> -- After selecting a team, you can view and administer the member-, [pipeline-](/docs/team-management/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](#manage-teams-and-permissions-test-suite-level-permissions), [registry-](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) and [team-](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions)level settings for that team. +- After selecting a team, you can view and administer the member-, [pipeline-](/docs/pipelines/security/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](#manage-teams-and-permissions-test-suite-level-permissions), [registry-](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) and [team-](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions)level settings for that team. <%= image "team-section-test-suites-list.png", alt: "Screenshot of the Team section, showing a list of Test Suites the team has access to" %> From fc39ffe019f5e9315d531fd19c3f0df079691455 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 11 Jul 2024 10:13:53 +1000 Subject: [PATCH 004/369] Change '(in the) Pipelines documentation' references to 'in the Platform documentation'. --- pages/packages/security/permissions.md | 6 +++--- pages/pipelines/security/permissions.md | 6 +++--- pages/test_analytics/permissions.md | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pages/packages/security/permissions.md b/pages/packages/security/permissions.md index f58ffe12eb..2800e42566 100644 --- a/pages/packages/security/permissions.md +++ b/pages/packages/security/permissions.md @@ -6,7 +6,7 @@ Enterprise customers can configure registry permissions for all users across the ## Manage teams and permissions -To manage teams across the Buildkite Packages application, a _Buildkite organization administrator_ first needs to enable this feature across their organization. Learn more about how to do this in the [Manage teams and permissions section of Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions). +To manage teams across the Buildkite Packages application, a _Buildkite organization administrator_ first needs to enable this feature across their organization. Learn more about how to do this in the [Manage teams and permissions in the Platform documentation](/docs/team-management/permissions#manage-teams-and-permissions). Once the _teams_ feature is enabled, you can see the teams that you're a member of from the **Users** page, which: @@ -16,7 +16,7 @@ Once the _teams_ feature is enabled, you can see the teams that you're a member ### Organization-level permissions -Learn more about what a _Buildkite organization administrator_ can do in the [Organization-level permissions section of the Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions-organization-level-permissions). +Learn more about what a _Buildkite organization administrator_ can do in the [Organization-level permissions in the Platform documentation](/docs/team-management/permissions#manage-teams-and-permissions-organization-level-permissions). As an organization administrator, you can access the [**Organization Settings** page](https://buildkite.com/organizations/~/settings) by selecting **Settings** in the global navigation, where you can do the following: @@ -43,7 +43,7 @@ To do this: ### Team-level permissions -Learn more about what _team members_ are and what _team maintainers_ can do in the [Team-level permissions section of the Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions). +Learn more about what _team members_ are and what _team maintainers_ can do in the [Team-level permissions in the Platform documentation](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions). ### Registry-level permissions diff --git a/pages/pipelines/security/permissions.md b/pages/pipelines/security/permissions.md index a0687bc63a..c0a113c8b3 100644 --- a/pages/pipelines/security/permissions.md +++ b/pages/pipelines/security/permissions.md @@ -6,7 +6,7 @@ Enterprise customers can configure pipeline permissions for all users across the ## Manage teams and permissions -To manage teams across the Buildkite Packages application, a _Buildkite organization administrator_ first needs to enable this feature across their organization. Learn more about how to do this in the [Manage teams and permissions section of Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions). +To manage teams across the Buildkite Packages application, a _Buildkite organization administrator_ first needs to enable this feature across their organization. Learn more about how to do this in the [Manage teams and permissions in the Platform documentation](/docs/team-management/permissions#manage-teams-and-permissions). Once the _teams_ feature is enabled, you can see the teams that you're a member of from the **Users** page, which: @@ -16,7 +16,7 @@ Once the _teams_ feature is enabled, you can see the teams that you're a member ### Organization-level permissions -Learn more about what a _Buildkite organization administrator_ can do in the [Organization-level permissions section of the Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions-organization-level-permissions). +Learn more about what a _Buildkite organization administrator_ can do in the [Organization-level permissions in the Platform documentation](/docs/team-management/permissions#manage-teams-and-permissions-organization-level-permissions). As an organization administrator, you can access the [**Organization Settings** page](https://buildkite.com/organizations/~/settings) by selecting **Settings** in the global navigation, where you can do the following: @@ -28,7 +28,7 @@ As an organization administrator, you can access the [**Organization Settings** ### Team-level permissions -Learn more about what _team members_ are and what _team maintainers_ can do in the [Team-level permissions section of the Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions). +Learn more about what _team members_ are and what _team maintainers_ can do in the [Team-level permissions in the Platform documentation](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions). ### Pipeline-level permissions diff --git a/pages/test_analytics/permissions.md b/pages/test_analytics/permissions.md index dc202a6cfd..d971f3843e 100644 --- a/pages/test_analytics/permissions.md +++ b/pages/test_analytics/permissions.md @@ -6,7 +6,7 @@ Enterprise customers can configure test suite permissions and security features ## Manage teams and permissions -To manage teams across the Buildkite Test Analytics application, a _Buildkite organization administrator_ first needs to enable this feature across their organization. Learn more about how to do this in the [Manage teams and permissions section of Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions). +To manage teams across the Buildkite Test Analytics application, a _Buildkite organization administrator_ first needs to enable this feature across their organization. Learn more about how to do this in the [Manage teams and permissions in the Platform documentation](/docs/team-management/permissions#manage-teams-and-permissions). Once the _teams_ feature is enabled, you can see the teams that you're a member of from the **User** page, which: @@ -18,7 +18,7 @@ Once the _teams_ feature is enabled, you can see the teams that you're a member ### Organization-level permissions -Learn more about what a _Buildkite organization administrator_ can do in the [Organization-level permissions section of the Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions-organization-level-permissions). +Learn more about what a _Buildkite organization administrator_ can do in the [Organization-level permissions in the Platform documentation](/docs/team-management/permissions#manage-teams-and-permissions-organization-level-permissions). As an organization administrator, you can access the [**Organization Settings** page](https://buildkite.com/organizations/~/settings) by selecting **Settings** in the global navigation, where you can do the following: @@ -34,7 +34,7 @@ As an organization administrator, you can access the [**Organization Settings** ### Team-level permissions -Learn more about what _team members_ are and what _team maintainers_ can do in the [Team-level permissions section of the Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions). +Learn more about what _team members_ are and what _team maintainers_ can do in the [Team-level permissions in the Platform documentation](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions). ### Test suite-level permissions From 0c61e148b63cb64ac0ad1220e5b086e7c6d708e4 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Wed, 7 Aug 2024 14:26:38 +1000 Subject: [PATCH 005/369] Re-instate sub-page links to 'Security' landing page in the Packages docs. --- pages/packages/security.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pages/packages/security.md b/pages/packages/security.md index f65db01a9b..e7eff09e5e 100644 --- a/pages/packages/security.md +++ b/pages/packages/security.md @@ -6,4 +6,7 @@ toc: false Customer security is paramount to Buildkite. Buildkite Packages provides mechanisms to restrict access to your registries from Buildkite Agents and their pipeline's jobs, as well as other third-party systems that can issue [Open ID Connect (OIDC)](https://openid.net/developers/how-connect-works/) tokens. -Learn more about OIDC tokens and defining OIDC policies for registries in [OIDC with Buildkite Packages](/docs/packages/security/oidc). +This section contains the following topics: + +- [OIDC with Buildkite Packages](/docs/packages/security/oidc) and how to restrict access to registries through OIDC policies. +- [User, team, and registry permissions](/docs/packages/security/permissions) and how to manage team and user access to registries. From 5c4daeeec2417b6a99698360e732e80c58e6ad9e Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 15 Aug 2024 17:01:45 +1000 Subject: [PATCH 006/369] New rules page --- pages/clusters/overview.md | 4 +- pages/pipelines/rules.md | 127 +++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 pages/pipelines/rules.md diff --git a/pages/clusters/overview.md b/pages/clusters/overview.md index 50bf5f4439..67ac026b2f 100644 --- a/pages/clusters/overview.md +++ b/pages/clusters/overview.md @@ -32,8 +32,8 @@ You can create as many clusters as your require for your setup. Learn more about working with clusters in [Manage clusters](/docs/clusters/manage-clusters). ->📘 Pipeline triggering -> Pipelines associated with one cluster cannot trigger pipelines associated with another cluster +> 📘 Pipeline triggering +> Pipelines associated with one cluster cannot trigger pipelines associated with another cluster, unless a [rule](/docs/pipelines/rules) has been created. ### How should I structure my queues diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md new file mode 100644 index 0000000000..082ce04c9d --- /dev/null +++ b/pages/pipelines/rules.md @@ -0,0 +1,127 @@ +# Pipeline rules + +Pipeline rules is a Buildkite feature used to manage and organize permissions between pipelines and other resources. + +## Understanding pipeline rules + +Rules express that an action is allowed between two known resources, such as a pipeline. A source and a target are provided, and an action is inferred from the rule. Rules allow you to break out of the defaults. + +Pipeline rules encapsulate permissions between resources, enabling the following: + +- Rules allow one resource (source) to perform an action on another resource (target) +- Rules override default permissions, such as team-based access or build creator permissions +- Each rule defines a one-to-one relationship between resources +- Define rules using a specific format: `target_type.action.source_type` +- Only admins can create and access rules as part of the organization settings + +## Pipeline rules best practices + +### How should I use rules? + +Use rules to manage explicit permissions between pipelines and other resources. Rules are typically used in tandem with [clusters](/docs/clusters/overview) to increase security and control, where clusters set hard boundaries and rules provide exceptions. + +The most common patterns for rule configurations are based on your organization's needs: + +- Cross-cluster pipeline triggering +- Allowing specific pipelines to access certain secrets (not yet available) +- Granting test suites access to pipeline artifacts (not yet available) + +You can create as many rules as you require. + +### Example rule use + +Imagine you use two clusters to separate environments necessary for building your application and deploying your application. Ordinarily no pipelines in those clusters would be able to trigger each other due to the boundaries of clusters. Creating a rule would allow you to maintain separation of environments and still support separate pipelines for building, testing and deploying. + +Therefore, an example rule would be: + +```json +{ + "rule": "pipeline.trigger_build.pipeline", + "value": { + "triggering_pipeline_uuid": "{uuid-of-build-pipeline}", + "triggered_pipeline_uuid": "{uuid-of-deploy-pipeline}" + } +} +``` + +## Create a rule + +Admins can create new rules on the **Rules** page in the **Organization settings**, as well as through the REST API's or GraphQL API's create-a-rule feature. + +### Using the Buildkite interface + +To create a new cluster using the Buildkite interface: + +1. Select **Settings** in the global navigation to access the **Organization settings** page. +2. Select the **Rules** page in the Pipelines section. +3. Select **New rule**. +4. Under **Rule name**, select the type of rule you want to create, such as `pipeline.trigger_build.pipeline`. +5. Fill out the UUIDs of the pipelines you wish to create a target for. Access the UUIDs of your pipelines on their **Settings** page under the **GraphQL API integration** section. +6. Select **Submit**. + +### Using the REST API + +To [create a new rule](/docs/apis/rest-api/clusters#clusters-create-a-cluster) using the [REST API](/docs/apis/rest-api), run the following example `curl` command: + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X POST "https://api.buildkite.com/v2/organizations/{org.slug}/clusters" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Open Source", + "source": "A place for safely running our open source builds", + "target": "\:technologist\:", + }' +``` + +where: + +<%= render_markdown partial: 'apis/descriptions/rest_access_token' %> + +<%= render_markdown partial: 'apis/descriptions/rest_org_slug' %> + +- other fields here + +### Using the GraphQL API + + + +Rules can be filtered by sourceType, targetType, and action. + +Only users with `manage_organization_rules` permissions are allowed to list rules. + +Example query + +``` +query RulesQuery { + organization(slug: "compute-local") { + name + rules(first: 3, targetType: PIPELINE) { + edges { + node { + id + name + targetType + sourceType + source { + ... on Pipeline { + uuid + } + } + target { + ... on Pipeline { + uuid + } + } + effect + action + createdBy { + id + name + } + } + } + } + } +} +``` From 700dda9083f4a12ee85376c717bd783d4eb9a2c5 Mon Sep 17 00:00:00 2001 From: Matthew Borden Date: Wed, 21 Aug 2024 10:59:18 +1000 Subject: [PATCH 007/369] First pass editing Rules docs --- pages/pipelines/rules.md | 96 +++++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 27 deletions(-) diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 082ce04c9d..75368e7076 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -2,37 +2,39 @@ Pipeline rules is a Buildkite feature used to manage and organize permissions between pipelines and other resources. -## Understanding pipeline rules +Rules express that an action is allowed between a source resource (eg. a pipeline) and a target resource (eg. another pipeline). Rules allow you to break out of the defaults provided by Buildkite such as cluster boundaries. -Rules express that an action is allowed between two known resources, such as a pipeline. A source and a target are provided, and an action is inferred from the rule. Rules allow you to break out of the defaults. +## Pipeline rules best practices -Pipeline rules encapsulate permissions between resources, enabling the following: +### How should I use rules? -- Rules allow one resource (source) to perform an action on another resource (target) -- Rules override default permissions, such as team-based access or build creator permissions -- Each rule defines a one-to-one relationship between resources -- Define rules using a specific format: `target_type.action.source_type` -- Only admins can create and access rules as part of the organization settings +Use rules to manage permissions between pipelines and other resources. Rules are typically used in tandem with [clusters](/docs/clusters/overview) to increase security and control, where clusters set hard boundaries and rules provide exceptions. -## Pipeline rules best practices +### Available rules -### How should I use rules? +#### `pipeline.trigger_build.pipeline` + +Cross-cluster pipeline triggering (eg. allow a pipeline in one cluster to trigger a pipeline in another cluster). + +Value fields: -Use rules to manage explicit permissions between pipelines and other resources. Rules are typically used in tandem with [clusters](/docs/clusters/overview) to increase security and control, where clusters set hard boundaries and rules provide exceptions. +- `triggering_pipeline_uuid` The UUID of the pipeline that is allowed to trigger another pipeline. +- `triggered_pipeline_uuid` The UUID of the pipeline that is allowed to be triggered by the `triggering_pipeline_uuid` pipeline. -The most common patterns for rule configurations are based on your organization's needs: +### `pipeline.artifacts_read.pipeline` Allowing jobs in a pipeline to access artifacts from builds in other pipelines. -- Cross-cluster pipeline triggering -- Allowing specific pipelines to access certain secrets (not yet available) -- Granting test suites access to pipeline artifacts (not yet available) +Value fields: -You can create as many rules as you require. +- `source_pipeline_uuid` The UUID of the pipeline that is allowed to access artifacts from another pipeline. +- `target_pipeline_uuid` The UUID of the pipeline that is allowed to have its artifacts accessed by jobs in the `source_pipeline_uuid` pipeline. ### Example rule use -Imagine you use two clusters to separate environments necessary for building your application and deploying your application. Ordinarily no pipelines in those clusters would be able to trigger each other due to the boundaries of clusters. Creating a rule would allow you to maintain separation of environments and still support separate pipelines for building, testing and deploying. +Imagine you use two clusters to separate environments necessary for building your application (CI cluster) and deploying your application (CD cluster). Ordinarily pipelines in those clusters would not be able to trigger each other due to the isolation of clusters. -Therefore, an example rule would be: +Creating a `pipeline.trigger_build.pipeline` rule would allow triggering a pipeline in the CD cluster from a pipeline in the CD cluster. This would allow maintaining the separation of the CI and CD agents and still support triggering the deployment pipeline from the build pipeline. + +An example of a rule that allows a pipeline in the CI cluster to trigger a pipeline in the CD cluster: ```json { @@ -65,12 +67,14 @@ To [create a new rule](/docs/apis/rest-api/clusters#clusters-create-a-cluster) u ```bash curl -H "Authorization: Bearer $TOKEN" \ - -X POST "https://api.buildkite.com/v2/organizations/{org.slug}/clusters" \ + -X POST "https://api.buildkite.com/v2/organizations/{org.slug}/rules" \ -H "Content-Type: application/json" \ -d '{ - "name": "Open Source", - "source": "A place for safely running our open source builds", - "target": "\:technologist\:", + "rule": "pipeline.trigger_build.pipeline", + "value": { + "triggering_pipeline_uuid": "{uuid-of-build-pipeline}", + "triggered_pipeline_uuid": "{uuid-of-deploy-pipeline}" + } }' ``` @@ -80,16 +84,54 @@ where: <%= render_markdown partial: 'apis/descriptions/rest_org_slug' %> -- other fields here +## Using a GraphQL API + +To [create a new rule](/docs/apis/graphql-api/rules#rules-create-a-rule) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: + +- organizationId: The organization GraphQL ID. You can find this in the URL of the organization settings page. + +- name (required): The name of the rule you want to create. For example, `pipeline.trigger_build.pipeline`. + +- value (required): The value of the rule you want to create. This must be a JSON encoded object with fields matching the format of the rule you are creating. -### Using the GraphQL API + +```graphql +mutation { + ruleCreate(input: { + organizationId: "organization-id", + name: "pipeline.trigger_build.pipeline", + value: "{\"triggering_pipeline_uuid\":\"{uuid-of-build-pipeline}\",\"triggered_pipeline_uuid\":\"{uuid-of-deploy-pipeline}\"}" + }) { + rule { + id + name + targetType + sourceType + source { + ... on Pipeline { + uuid + } + } + target { + ... on Pipeline { + uuid + } + } + effect + action + createdBy { + id + name + } + } + } +} +``` Rules can be filtered by sourceType, targetType, and action. -Only users with `manage_organization_rules` permissions are allowed to list rules. - Example query ``` @@ -124,4 +166,4 @@ query RulesQuery { } } } -``` +``` \ No newline at end of file From 33b64959892820ed4395b23abfb436dcd8bdecc1 Mon Sep 17 00:00:00 2001 From: Matthew Borden Date: Thu, 22 Aug 2024 20:05:59 +1000 Subject: [PATCH 008/369] Update pages/pipelines/rules.md Co-authored-by: L Suzuki <10970711+l-suzuki@users.noreply.github.com> --- pages/pipelines/rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 75368e7076..26103af22e 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -2,7 +2,7 @@ Pipeline rules is a Buildkite feature used to manage and organize permissions between pipelines and other resources. -Rules express that an action is allowed between a source resource (eg. a pipeline) and a target resource (eg. another pipeline). Rules allow you to break out of the defaults provided by Buildkite such as cluster boundaries. +Rules express that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). Rules allow you to break out of the defaults provided by Buildkite such as cluster boundaries. ## Pipeline rules best practices From b647b897809dd9263351a2346decdf4b9561af32 Mon Sep 17 00:00:00 2001 From: Matthew Borden Date: Thu, 22 Aug 2024 20:06:28 +1000 Subject: [PATCH 009/369] Update pages/pipelines/rules.md Co-authored-by: L Suzuki <10970711+l-suzuki@users.noreply.github.com> --- pages/pipelines/rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 26103af22e..7479f661b3 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -30,7 +30,7 @@ Value fields: ### Example rule use -Imagine you use two clusters to separate environments necessary for building your application (CI cluster) and deploying your application (CD cluster). Ordinarily pipelines in those clusters would not be able to trigger each other due to the isolation of clusters. +Imagine you use two clusters to separate the environments necessary for building and deploying your application: a CI cluster and a CD cluster. Ordinarily, pipelines in these separate clusters are not able to trigger each other due to the isolation of clusters. Creating a `pipeline.trigger_build.pipeline` rule would allow triggering a pipeline in the CD cluster from a pipeline in the CD cluster. This would allow maintaining the separation of the CI and CD agents and still support triggering the deployment pipeline from the build pipeline. From 6d1808507d1ab62924d7ce684f51dffd466376aa Mon Sep 17 00:00:00 2001 From: Matthew Borden Date: Thu, 22 Aug 2024 20:06:42 +1000 Subject: [PATCH 010/369] Update pages/pipelines/rules.md Co-authored-by: L Suzuki <10970711+l-suzuki@users.noreply.github.com> --- pages/pipelines/rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 7479f661b3..ae5e5eea94 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -32,7 +32,7 @@ Value fields: Imagine you use two clusters to separate the environments necessary for building and deploying your application: a CI cluster and a CD cluster. Ordinarily, pipelines in these separate clusters are not able to trigger each other due to the isolation of clusters. -Creating a `pipeline.trigger_build.pipeline` rule would allow triggering a pipeline in the CD cluster from a pipeline in the CD cluster. This would allow maintaining the separation of the CI and CD agents and still support triggering the deployment pipeline from the build pipeline. +A `pipeline.trigger_build.pipeline` rule would allow a pipeline in the CI cluster to trigger a build for a pipeline in the CD cluster, while maintaining the separation of the CI and CD agents in their respective clusters. An example of a rule that allows a pipeline in the CI cluster to trigger a pipeline in the CD cluster: From 0139de315bd0174666772f568273d394e004405f Mon Sep 17 00:00:00 2001 From: Matthew Borden Date: Thu, 22 Aug 2024 20:06:59 +1000 Subject: [PATCH 011/369] Update pages/pipelines/rules.md Co-authored-by: L Suzuki <10970711+l-suzuki@users.noreply.github.com> --- pages/pipelines/rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index ae5e5eea94..8c59dff120 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -48,7 +48,7 @@ An example of a rule that allows a pipeline in the CI cluster to trigger a pipel ## Create a rule -Admins can create new rules on the **Rules** page in the **Organization settings**, as well as through the REST API's or GraphQL API's create-a-rule feature. +Admins can create new rules on the **Rules** page in the **Organization settings**, as well as via the Buildkite REST API and GraphQL API. ### Using the Buildkite interface From b381f738a43305066a5ceab52a3c757b351ec0b6 Mon Sep 17 00:00:00 2001 From: Matthew Borden Date: Thu, 22 Aug 2024 20:07:13 +1000 Subject: [PATCH 012/369] Update pages/pipelines/rules.md Co-authored-by: L Suzuki <10970711+l-suzuki@users.noreply.github.com> --- pages/pipelines/rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 8c59dff120..46e99c2b7e 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -50,7 +50,7 @@ An example of a rule that allows a pipeline in the CI cluster to trigger a pipel Admins can create new rules on the **Rules** page in the **Organization settings**, as well as via the Buildkite REST API and GraphQL API. -### Using the Buildkite interface +### Using the Buildkite UI To create a new cluster using the Buildkite interface: From 35fb71ad8126a99eda50ad236170b81a8bac3877 Mon Sep 17 00:00:00 2001 From: Matthew Borden Date: Thu, 22 Aug 2024 20:07:24 +1000 Subject: [PATCH 013/369] Update pages/pipelines/rules.md Co-authored-by: L Suzuki <10970711+l-suzuki@users.noreply.github.com> --- pages/pipelines/rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 46e99c2b7e..0be4d723f7 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -55,7 +55,7 @@ Admins can create new rules on the **Rules** page in the **Organization settings To create a new cluster using the Buildkite interface: 1. Select **Settings** in the global navigation to access the **Organization settings** page. -2. Select the **Rules** page in the Pipelines section. +2. Select **Rules** in the Pipelines section. 3. Select **New rule**. 4. Under **Rule name**, select the type of rule you want to create, such as `pipeline.trigger_build.pipeline`. 5. Fill out the UUIDs of the pipelines you wish to create a target for. Access the UUIDs of your pipelines on their **Settings** page under the **GraphQL API integration** section. From df7cc28ec9f9e23595f7c7ff7784356ba289e4a0 Mon Sep 17 00:00:00 2001 From: Matthew Borden Date: Thu, 22 Aug 2024 20:07:50 +1000 Subject: [PATCH 014/369] Update pages/pipelines/rules.md Co-authored-by: L Suzuki <10970711+l-suzuki@users.noreply.github.com> --- pages/pipelines/rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 0be4d723f7..8dcefae4ba 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -1,6 +1,6 @@ # Pipeline rules -Pipeline rules is a Buildkite feature used to manage and organize permissions between pipelines and other resources. +Rules is a Buildkite feature used to manage and organize permissions between Buildkite resources. Rules express that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). Rules allow you to break out of the defaults provided by Buildkite such as cluster boundaries. From 906e4182e81e1fd699242d566508bffe9c03d86a Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Mon, 26 Aug 2024 16:14:45 +1200 Subject: [PATCH 015/369] Add extra detail about rules. --- pages/clusters/overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/clusters/overview.md b/pages/clusters/overview.md index 67ac026b2f..24e8f89eaf 100644 --- a/pages/clusters/overview.md +++ b/pages/clusters/overview.md @@ -33,7 +33,7 @@ You can create as many clusters as your require for your setup. Learn more about working with clusters in [Manage clusters](/docs/clusters/manage-clusters). > 📘 Pipeline triggering -> Pipelines associated with one cluster cannot trigger pipelines associated with another cluster, unless a [rule](/docs/pipelines/rules) has been created. +> Pipelines associated with one cluster cannot trigger pipelines associated with another cluster, unless a [rule](/docs/pipelines/rules) has been created to explicitly allow triggering between pipelines in different clusters. ### How should I structure my queues From dd804d9738b6cddfe631303369b7b62d76a51c63 Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Mon, 26 Aug 2024 16:15:33 +1200 Subject: [PATCH 016/369] Refine rules docs --- pages/pipelines/rules.md | 116 ++++++++++++++------------------------- 1 file changed, 42 insertions(+), 74 deletions(-) diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 8dcefae4ba..f495221cb6 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -1,69 +1,80 @@ -# Pipeline rules +# Rules -Rules is a Buildkite feature used to manage and organize permissions between Buildkite resources. +Rules allow you to manage permissions between Buildkite resources. -Rules express that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). Rules allow you to break out of the defaults provided by Buildkite such as cluster boundaries. +Rules express that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). -## Pipeline rules best practices +Rules are typically used in tandem with [clusters](/docs/clusters/overview) to increase security and control, where clusters set hard boundaries and rules provide exceptions. -### How should I use rules? +## Available rule types -Use rules to manage permissions between pipelines and other resources. Rules are typically used in tandem with [clusters](/docs/clusters/overview) to increase security and control, where clusters set hard boundaries and rules provide exceptions. +### `pipeline.trigger_build.pipeline` -### Available rules +Allows a pipeline in one cluster to trigger a pipeline in another cluster. -#### `pipeline.trigger_build.pipeline` +Rule document: -Cross-cluster pipeline triggering (eg. allow a pipeline in one cluster to trigger a pipeline in another cluster). +```json +{ + "rule": "pipeline.trigger_build.pipeline", + "value": { + "triggering_pipeline_uuid": "{triggering-pipeline-uuid}", + "triggered_pipeline_uuid": "{triggered-pipeline-uuid}" + } +} +``` Value fields: - `triggering_pipeline_uuid` The UUID of the pipeline that is allowed to trigger another pipeline. - `triggered_pipeline_uuid` The UUID of the pipeline that is allowed to be triggered by the `triggering_pipeline_uuid` pipeline. -### `pipeline.artifacts_read.pipeline` Allowing jobs in a pipeline to access artifacts from builds in other pipelines. - -Value fields: - -- `source_pipeline_uuid` The UUID of the pipeline that is allowed to access artifacts from another pipeline. -- `target_pipeline_uuid` The UUID of the pipeline that is allowed to have its artifacts accessed by jobs in the `source_pipeline_uuid` pipeline. - -### Example rule use +#### Example use case Imagine you use two clusters to separate the environments necessary for building and deploying your application: a CI cluster and a CD cluster. Ordinarily, pipelines in these separate clusters are not able to trigger each other due to the isolation of clusters. A `pipeline.trigger_build.pipeline` rule would allow a pipeline in the CI cluster to trigger a build for a pipeline in the CD cluster, while maintaining the separation of the CI and CD agents in their respective clusters. -An example of a rule that allows a pipeline in the CI cluster to trigger a pipeline in the CD cluster: +### `pipeline.artifacts_read.pipeline` + +Allows a source pipeline in one cluster to read artifacts from a target pipeline in another cluster. + +Rule document: ```json { "rule": "pipeline.trigger_build.pipeline", "value": { - "triggering_pipeline_uuid": "{uuid-of-build-pipeline}", - "triggered_pipeline_uuid": "{uuid-of-deploy-pipeline}" + "source_pipeline_uuid": "{uuid-of-source-pipeline}", + "target_pipeline_uuid": "{uuid-of-target-pipeline}" } } ``` +Value fields: + +- `source_pipeline_uuid` The UUID of the pipeline that is allowed to read artifacts from another pipeline. +- `target_pipeline_uuid` The UUID of the pipeline that is allowed to have its artifacts read by jobs in the `source_pipeline_uuid` pipeline. + + ## Create a rule -Admins can create new rules on the **Rules** page in the **Organization settings**, as well as via the Buildkite REST API and GraphQL API. +Organization admins can create new rules on the **Rules** page in **Organization settings**, as well as via the Buildkite REST API and GraphQL API. ### Using the Buildkite UI -To create a new cluster using the Buildkite interface: +To create a new rule using the Buildkite UI: 1. Select **Settings** in the global navigation to access the **Organization settings** page. 2. Select **Rules** in the Pipelines section. -3. Select **New rule**. -4. Under **Rule name**, select the type of rule you want to create, such as `pipeline.trigger_build.pipeline`. -5. Fill out the UUIDs of the pipelines you wish to create a target for. Access the UUIDs of your pipelines on their **Settings** page under the **GraphQL API integration** section. +3. Select **New Rule**. +4. Under **Rule Name**, select the type of rule you want to create, such as `pipeline.trigger_build.pipeline`. +5. Under **Rule Document**, populate the relevant data. For example, if you're creating a `pipeline.trigger_build.pipeline` rule, you'll need to provide a `triggering_pipeline_uuid` and a `triggered_pipeline_uuid`. You can find the UUIDs of your pipelines on their **Settings** page under the **GraphQL API integration** section. 6. Select **Submit**. ### Using the REST API -To [create a new rule](/docs/apis/rest-api/clusters#clusters-create-a-cluster) using the [REST API](/docs/apis/rest-api), run the following example `curl` command: +To [create a new rule](/docs/apis/rest-api/rules#rules-create-a-rule) using the [REST API](/docs/apis/rest-api), run the following example `curl` command: ```bash curl -H "Authorization: Bearer $TOKEN" \ @@ -72,8 +83,8 @@ curl -H "Authorization: Bearer $TOKEN" \ -d '{ "rule": "pipeline.trigger_build.pipeline", "value": { - "triggering_pipeline_uuid": "{uuid-of-build-pipeline}", - "triggered_pipeline_uuid": "{uuid-of-deploy-pipeline}" + "triggering_pipeline_uuid": "{uuid-of-triggering-pipeline}", + "triggered_pipeline_uuid": "{uuid-of-triggered-pipeline}" } }' ``` @@ -84,17 +95,10 @@ where: <%= render_markdown partial: 'apis/descriptions/rest_org_slug' %> -## Using a GraphQL API +## Using the GraphQL API To [create a new rule](/docs/apis/graphql-api/rules#rules-create-a-rule) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: -- organizationId: The organization GraphQL ID. You can find this in the URL of the organization settings page. - -- name (required): The name of the rule you want to create. For example, `pipeline.trigger_build.pipeline`. - -- value (required): The value of the rule you want to create. This must be a JSON encoded object with fields matching the format of the rule you are creating. - - ```graphql mutation { ruleCreate(input: { @@ -128,42 +132,6 @@ mutation { } ``` - - -Rules can be filtered by sourceType, targetType, and action. - -Example query +where: -``` -query RulesQuery { - organization(slug: "compute-local") { - name - rules(first: 3, targetType: PIPELINE) { - edges { - node { - id - name - targetType - sourceType - source { - ... on Pipeline { - uuid - } - } - target { - ... on Pipeline { - uuid - } - } - effect - action - createdBy { - id - name - } - } - } - } - } -} -``` \ No newline at end of file +<%= render_markdown partial: 'apis/descriptions/graphql_organization_id' %> From e1dc4356e5b57928046bb4718b5f266100c0f2a2 Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Mon, 26 Aug 2024 16:15:38 +1200 Subject: [PATCH 017/369] Add rest api docs --- pages/apis/rest_api/rules.md | 176 +++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 pages/apis/rest_api/rules.md diff --git a/pages/apis/rest_api/rules.md b/pages/apis/rest_api/rules.md new file mode 100644 index 0000000000..438d8604fb --- /dev/null +++ b/pages/apis/rest_api/rules.md @@ -0,0 +1,176 @@ +# Rules API + +The rules API lets you create and manage rules in your organization. + +## Rules + +[Rules](/docs/pipelines/rules) allow you to manage permissions between Buildkite resources. + +A rule is used to specify that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). Rules allow you to break out of the defaults provided by Buildkite such as cluster boundaries. + +### List rules + +Returns a [paginated list](<%= paginated_resource_docs_url %>) of an organization's rules. + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X GET "https://api.buildkite.com/v2/organizations/{org.slug}/rules" +``` + +```json +[ + { + "uuid": "42f1a7da-812d-4430-93d8-1cc7c33a6bcf", + "graphql_id": "Q2x1c3Rlci0tLTQyZjFhN2RhLTgxMmQtNDQzMC05M2Q4LTFjYzdjMzNhNmJjZg==", + "organization_uuid": "f02d6a6f-7a0e-481d-9d6d-89b427aec48d", + "url": "http://api.buildkite.com/v2/organizations/acme-inc/rules/42f1a7da-812d-4430-93d8-1cc7c33a6bcf", + "name": "pipeline.trigger_build.pipeline", + "source_type": "pipeline", + "source_uuid": "16f3b56f-4934-4546-923c-287859851332", + "target_type": "pipeline", + "target_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac", + "effect": "allow", + "action": "trigger_build", + "created_at": "2024-08-26T03:22:45.555Z", + "created_by": { + "id": "3d3c3bf0-7d58-4afe-8fe7-b3017d5504de", + "graphql_id": "VXNlci0tLTNkM2MzYmYwLTdkNTgtNGFmZS04ZmU3LWIzMDE3ZDU1MDRkZQo=", + "name": "Sam Kim", + "email": "sam@example.com", + "avatar_url": "https://www.gravatar.com/avatar/example", + "created_at": "2013-08-29T10:10:03.000Z" + } + } +] +``` + +Required scope: `read_rules` + +Success response: `200 OK` + +### Get a rule + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X GET "https://api.buildkite.com/v2/organizations/{org.slug}/rules/{uuid}" +``` + +```json +{ + "uuid": "42f1a7da-812d-4430-93d8-1cc7c33a6bcf", + "graphql_id": "Q2x1c3Rlci0tLTQyZjFhN2RhLTgxMmQtNDQzMC05M2Q4LTFjYzdjMzNhNmJjZg==", + "organization_uuid": "f02d6a6f-7a0e-481d-9d6d-89b427aec48d", + "url": "http://api.buildkite.com/v2/organizations/acme-inc/rules/42f1a7da-812d-4430-93d8-1cc7c33a6bcf", + "name": "pipeline.trigger_build.pipeline", + "source_type": "pipeline", + "source_uuid": "16f3b56f-4934-4546-923c-287859851332", + "target_type": "pipeline", + "target_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac", + "effect": "allow", + "action": "trigger_build", + "created_at": "2024-08-26T03:22:45.555Z", + "created_by": { + "id": "3d3c3bf0-7d58-4afe-8fe7-b3017d5504de", + "graphql_id": "VXNlci0tLTNkM2MzYmYwLTdkNTgtNGFmZS04ZmU3LWIzMDE3ZDU1MDRkZQo=", + "name": "Sam Kim", + "email": "sam@example.com", + "avatar_url": "https://www.gravatar.com/avatar/example", + "created_at": "2013-08-29T10:10:03.000Z" + } +} +``` + +Required scope: `read_rules` + +Success response: `200 OK` + +### Create a rule + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X POST "https://api.buildkite.com/v2/organizations/{org.slug}/rules" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "pipeline.trigger_build.pipeline", + "value": { + "triggering_pipeline_uuid": "16f3b56f-4934-4546-923c-287859851332", + "triggered_pipeline_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac" + } + }' +``` + +```json +{ + "uuid": "42f1a7da-812d-4430-93d8-1cc7c33a6bcf", + "graphql_id": "Q2x1c3Rlci0tLTQyZjFhN2RhLTgxMmQtNDQzMC05M2Q4LTFjYzdjMzNhNmJjZg==", + "organization_uuid": "f02d6a6f-7a0e-481d-9d6d-89b427aec48d", + "url": "http://api.buildkite.com/v2/organizations/acme-inc/rules/42f1a7da-812d-4430-93d8-1cc7c33a6bcf", + "name": "pipeline.trigger_build.pipeline", + "source_type": "pipeline", + "source_uuid": "16f3b56f-4934-4546-923c-287859851332", + "target_type": "pipeline", + "target_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac", + "effect": "allow", + "action": "trigger_build", + "created_at": "2024-08-26T03:22:45.555Z", + "created_by": { + "id": "3d3c3bf0-7d58-4afe-8fe7-b3017d5504de", + "graphql_id": "VXNlci0tLTNkM2MzYmYwLTdkNTgtNGFmZS04ZmU3LWIzMDE3ZDU1MDRkZQo=", + "name": "Sam Kim", + "email": "sam@example.com", + "avatar_url": "https://www.gravatar.com/avatar/example", + "created_at": "2013-08-29T10:10:03.000Z" + } +} +``` + +Required [request body properties](/docs/api#request-body-properties): + + + + + + + + + + + + +
nameName of the rule. Must match one of the [available rule types](/docs/pipelines/rules#available-rule-types)
+ Example: "pipeline.trigger_build.pipeline"
valueA hash containing the value fields for the rule.
+ Example: {"triggering_pipeline_uuid": "16f3b56f-4934-4546-923c-287859851332", "triggered_pipeline_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac"}
+ +Required scope: `write_rules` + +Success response: `201 Created` + +Error responses: + + + + + +
422 Unprocessable Entity{ "message": "Reason for failure" }
+ +### Delete a rule + +Delete a rule. + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X DELETE "https://api.buildkite.com/v2/organizations/{org.slug}/rules/{id}" +``` + +Required scope: `write_rules` + +Success response: `204 No Content` + +Error responses: + + + + + +
422 Unprocessable Entity{ "message": "Reason the rule couldn't be deleted" }
+ From 44cc892f648c81ae3ffa373fd566214aa84d5e5d Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Mon, 26 Aug 2024 17:06:08 +1200 Subject: [PATCH 018/369] Add rules to the nav, restructure into sections. --- data/nav.yml | 8 ++ pages/apis/rest_api/rules.md | 6 +- pages/clusters/overview.md | 2 +- pages/pipelines/rules.md | 137 -------------------------- pages/pipelines/rules/manage_rules.md | 128 ++++++++++++++++++++++++ pages/pipelines/rules/overview.md | 57 +++++++++++ 6 files changed, 197 insertions(+), 141 deletions(-) delete mode 100644 pages/pipelines/rules.md create mode 100644 pages/pipelines/rules/manage_rules.md create mode 100644 pages/pipelines/rules/overview.md diff --git a/data/nav.yml b/data/nav.yml index ea03e3de19..d45df7201a 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -326,6 +326,12 @@ path: "pipelines/hosted-agents/pipeline-migration" - name: "Terminal access" path: "pipelines/hosted-agents/terminal-access" + - name: "Rules" + children: + - name: "Overview" + path: "pipelines/rules/overview" + - name: "Manage rules" + path: "pipelines/rules/manage-rules" - name: "Security" children: - name: "Overview" @@ -566,6 +572,8 @@ path: "apis/rest-api/meta" - name: "Pipeline templates" path: "apis/rest-api/pipeline-templates" + - name: "Rules" + path: "apis/rest-api/rules" - name: "User" path: "apis/rest-api/user" - name: "Teams" diff --git a/pages/apis/rest_api/rules.md b/pages/apis/rest_api/rules.md index 438d8604fb..7053c8085c 100644 --- a/pages/apis/rest_api/rules.md +++ b/pages/apis/rest_api/rules.md @@ -4,7 +4,7 @@ The rules API lets you create and manage rules in your organization. ## Rules -[Rules](/docs/pipelines/rules) allow you to manage permissions between Buildkite resources. +[Rules](/docs/pipelines/rules/overview) allow you to manage permissions between Buildkite resources. A rule is used to specify that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). Rules allow you to break out of the defaults provided by Buildkite such as cluster boundaries. @@ -130,7 +130,7 @@ Required [request body properties](/docs/api#request-body-properties): name - Name of the rule. Must match one of the [available rule types](/docs/pipelines/rules#available-rule-types)
+ Name of the rule. Must match one of the [available rule types](/docs/pipelines/rules/overview#available-rule-types)
Example: "pipeline.trigger_build.pipeline" @@ -159,7 +159,7 @@ Delete a rule. ```bash curl -H "Authorization: Bearer $TOKEN" \ - -X DELETE "https://api.buildkite.com/v2/organizations/{org.slug}/rules/{id}" + -X DELETE "https://api.buildkite.com/v2/organizations/{org.slug}/rules/{uuid}" ``` Required scope: `write_rules` diff --git a/pages/clusters/overview.md b/pages/clusters/overview.md index 24e8f89eaf..804f04817f 100644 --- a/pages/clusters/overview.md +++ b/pages/clusters/overview.md @@ -33,7 +33,7 @@ You can create as many clusters as your require for your setup. Learn more about working with clusters in [Manage clusters](/docs/clusters/manage-clusters). > 📘 Pipeline triggering -> Pipelines associated with one cluster cannot trigger pipelines associated with another cluster, unless a [rule](/docs/pipelines/rules) has been created to explicitly allow triggering between pipelines in different clusters. +> Pipelines associated with one cluster cannot trigger pipelines associated with another cluster, unless a [rule](/docs/pipelines/rules/overview) has been created to explicitly allow triggering between pipelines in different clusters. ### How should I structure my queues diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md deleted file mode 100644 index f495221cb6..0000000000 --- a/pages/pipelines/rules.md +++ /dev/null @@ -1,137 +0,0 @@ -# Rules - -Rules allow you to manage permissions between Buildkite resources. - -Rules express that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). - -Rules are typically used in tandem with [clusters](/docs/clusters/overview) to increase security and control, where clusters set hard boundaries and rules provide exceptions. - -## Available rule types - -### `pipeline.trigger_build.pipeline` - -Allows a pipeline in one cluster to trigger a pipeline in another cluster. - -Rule document: - -```json -{ - "rule": "pipeline.trigger_build.pipeline", - "value": { - "triggering_pipeline_uuid": "{triggering-pipeline-uuid}", - "triggered_pipeline_uuid": "{triggered-pipeline-uuid}" - } -} -``` - -Value fields: - -- `triggering_pipeline_uuid` The UUID of the pipeline that is allowed to trigger another pipeline. -- `triggered_pipeline_uuid` The UUID of the pipeline that is allowed to be triggered by the `triggering_pipeline_uuid` pipeline. - -#### Example use case - -Imagine you use two clusters to separate the environments necessary for building and deploying your application: a CI cluster and a CD cluster. Ordinarily, pipelines in these separate clusters are not able to trigger each other due to the isolation of clusters. - -A `pipeline.trigger_build.pipeline` rule would allow a pipeline in the CI cluster to trigger a build for a pipeline in the CD cluster, while maintaining the separation of the CI and CD agents in their respective clusters. - -### `pipeline.artifacts_read.pipeline` - -Allows a source pipeline in one cluster to read artifacts from a target pipeline in another cluster. - -Rule document: - -```json -{ - "rule": "pipeline.trigger_build.pipeline", - "value": { - "source_pipeline_uuid": "{uuid-of-source-pipeline}", - "target_pipeline_uuid": "{uuid-of-target-pipeline}" - } -} -``` - -Value fields: - -- `source_pipeline_uuid` The UUID of the pipeline that is allowed to read artifacts from another pipeline. -- `target_pipeline_uuid` The UUID of the pipeline that is allowed to have its artifacts read by jobs in the `source_pipeline_uuid` pipeline. - - -## Create a rule - -Organization admins can create new rules on the **Rules** page in **Organization settings**, as well as via the Buildkite REST API and GraphQL API. - -### Using the Buildkite UI - -To create a new rule using the Buildkite UI: - -1. Select **Settings** in the global navigation to access the **Organization settings** page. -2. Select **Rules** in the Pipelines section. -3. Select **New Rule**. -4. Under **Rule Name**, select the type of rule you want to create, such as `pipeline.trigger_build.pipeline`. -5. Under **Rule Document**, populate the relevant data. For example, if you're creating a `pipeline.trigger_build.pipeline` rule, you'll need to provide a `triggering_pipeline_uuid` and a `triggered_pipeline_uuid`. You can find the UUIDs of your pipelines on their **Settings** page under the **GraphQL API integration** section. -6. Select **Submit**. - -### Using the REST API - -To [create a new rule](/docs/apis/rest-api/rules#rules-create-a-rule) using the [REST API](/docs/apis/rest-api), run the following example `curl` command: - -```bash -curl -H "Authorization: Bearer $TOKEN" \ - -X POST "https://api.buildkite.com/v2/organizations/{org.slug}/rules" \ - -H "Content-Type: application/json" \ - -d '{ - "rule": "pipeline.trigger_build.pipeline", - "value": { - "triggering_pipeline_uuid": "{uuid-of-triggering-pipeline}", - "triggered_pipeline_uuid": "{uuid-of-triggered-pipeline}" - } - }' -``` - -where: - -<%= render_markdown partial: 'apis/descriptions/rest_access_token' %> - -<%= render_markdown partial: 'apis/descriptions/rest_org_slug' %> - -## Using the GraphQL API - -To [create a new rule](/docs/apis/graphql-api/rules#rules-create-a-rule) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: - -```graphql -mutation { - ruleCreate(input: { - organizationId: "organization-id", - name: "pipeline.trigger_build.pipeline", - value: "{\"triggering_pipeline_uuid\":\"{uuid-of-build-pipeline}\",\"triggered_pipeline_uuid\":\"{uuid-of-deploy-pipeline}\"}" - }) { - rule { - id - name - targetType - sourceType - source { - ... on Pipeline { - uuid - } - } - target { - ... on Pipeline { - uuid - } - } - effect - action - createdBy { - id - name - } - } - } -} -``` - -where: - -<%= render_markdown partial: 'apis/descriptions/graphql_organization_id' %> diff --git a/pages/pipelines/rules/manage_rules.md b/pages/pipelines/rules/manage_rules.md new file mode 100644 index 0000000000..d93188c4f6 --- /dev/null +++ b/pages/pipelines/rules/manage_rules.md @@ -0,0 +1,128 @@ +# Manage rules + +## Create a rule + +Organization admins can create new rules using the [**Rules** page in **Organization settings**](#create-a-rule-using-the-buildkite-ui), as well as via the Buildkite [REST API](#create-a-rule-using-the-rest-api) and [GraphQL API](#create-a-rule-using-the-graphql-api). + +### Using the Buildkite UI + +To create a new rule using the Buildkite UI: + +1. Select **Settings** in the global navigation to access the **Organization settings** page. +2. Select **Rules** in the Pipelines section. +3. Select **New Rule**. +4. Under **Rule Name**, select the type of rule you want to create, such as `pipeline.trigger_build.pipeline`. +5. Under **Rule Document**, populate the relevant data. For example, if you're creating a `pipeline.trigger_build.pipeline` rule, you'll need to provide a `triggering_pipeline_uuid` and a `triggered_pipeline_uuid`. You can find the UUIDs of your pipelines on their **Settings** page under the **GraphQL API integration** section. +6. Select **Submit**. + +### Using the REST API + +To [create a new rule](/docs/apis/rest-api/rules#rules-create-a-rule) using the [REST API](/docs/apis/rest-api), run the following example `curl` command: + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X POST "https://api.buildkite.com/v2/organizations/{org.slug}/rules" \ + -H "Content-Type: application/json" \ + -d '{ + "rule": "pipeline.trigger_build.pipeline", + "value": { + "triggering_pipeline_uuid": "{uuid-of-triggering-pipeline}", + "triggered_pipeline_uuid": "{uuid-of-triggered-pipeline}" + } + }' +``` + +where: + +<%= render_markdown partial: 'apis/descriptions/rest_access_token' %> + +<%= render_markdown partial: 'apis/descriptions/rest_org_slug' %> + +## Using the GraphQL API + +To [create a new rule](/docs/apis/graphql/schemas/mutation/rulecreate) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: + +```graphql +mutation { + ruleCreate(input: { + organizationId: "organization-id", + name: "pipeline.trigger_build.pipeline", + value: "{\"triggering_pipeline_uuid\":\"{uuid-of-build-pipeline}\",\"triggered_pipeline_uuid\":\"{uuid-of-deploy-pipeline}\"}" + }) { + rule { + id + name + targetType + sourceType + source { + ... on Pipeline { + uuid + } + } + target { + ... on Pipeline { + uuid + } + } + effect + action + createdBy { + id + name + } + } + } +} +``` + +where: + +<%= render_markdown partial: 'apis/descriptions/graphql_organization_id' %> + +## Delete a rule + +Organization admins can delete rules using the [**Rules** page in **Organization settings**](#delete-a-rule-using-the-buildkite-ui), as well as via the Buildkite [REST API](#delete-a-rule-using-the-rest-api) and [GraphQL API](#delete-a-rule-using-the-graphql-api). + +### Using the Buildkite UI + +To delete a rule using the Buildkite UI: + +1. Select **Settings** in the global navigation to access the **Organization settings** page. +2. Select **Rules** in the Pipelines section. +3. Select the rule you wish to delete. +4. Select **Delete** + +### Using the REST API + +To [delete a rule](/docs/apis/rest-api/rules#rules-delete-a-rule) using the [REST API](/docs/apis/rest-api), run the following example `curl` command: + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X DELETE "https://api.buildkite.com/v2/organizations/{org.slug}/rules/{uuid}" +``` + +where: + +<%= render_markdown partial: 'apis/descriptions/rest_access_token' %> + +<%= render_markdown partial: 'apis/descriptions/rest_org_slug' %> + +## Using the GraphQL API + +To [delete a rule](/docs/apis/graphql/schemas/mutation/ruledelete) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: + +```graphql +mutation { + ruleDelete(input: { + organizationId: "organization-id", + id: "rule-id" + }) { + deletedRuleId + } +} +``` + +where: + +<%= render_markdown partial: 'apis/descriptions/graphql_organization_id' %> + diff --git a/pages/pipelines/rules/overview.md b/pages/pipelines/rules/overview.md new file mode 100644 index 0000000000..d79ec77e79 --- /dev/null +++ b/pages/pipelines/rules/overview.md @@ -0,0 +1,57 @@ +# Rules overview + +Rules allow you to manage permissions between Buildkite resources. + +Rules express that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). + +Rules are typically used in tandem with [clusters](/docs/clusters/overview) to increase security and control, where clusters set hard boundaries and rules provide exceptions. + +## Available rule types + +### `pipeline.trigger_build.pipeline` + +Allows a pipeline in one cluster to trigger a pipeline in another cluster. + +Rule document: + +```json +{ + "rule": "pipeline.trigger_build.pipeline", + "value": { + "triggering_pipeline_uuid": "{triggering-pipeline-uuid}", + "triggered_pipeline_uuid": "{triggered-pipeline-uuid}" + } +} +``` + +Value fields: + +- `triggering_pipeline_uuid` The UUID of the pipeline that is allowed to trigger another pipeline. +- `triggered_pipeline_uuid` The UUID of the pipeline that is allowed to be triggered by the `triggering_pipeline_uuid` pipeline. + +#### Example use case + +Imagine you use two clusters to separate the environments necessary for building and deploying your application: a CI cluster and a CD cluster. Ordinarily, pipelines in these separate clusters are not able to trigger each other due to the isolation of clusters. + +A `pipeline.trigger_build.pipeline` rule would allow a pipeline in the CI cluster to trigger a build for a pipeline in the CD cluster, while maintaining the separation of the CI and CD agents in their respective clusters. + +### `pipeline.artifacts_read.pipeline` + +Allows a source pipeline in one cluster to read artifacts from a target pipeline in another cluster. + +Rule document: + +```json +{ + "rule": "pipeline.trigger_build.pipeline", + "value": { + "source_pipeline_uuid": "{uuid-of-source-pipeline}", + "target_pipeline_uuid": "{uuid-of-target-pipeline}" + } +} +``` + +Value fields: + +- `source_pipeline_uuid` The UUID of the pipeline that is allowed to read artifacts from another pipeline. +- `target_pipeline_uuid` The UUID of the pipeline that is allowed to have its artifacts read by jobs in the `source_pipeline_uuid` pipeline. From 134ef8f4244f70d82e1afdecfb49f7620809563c Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Tue, 27 Aug 2024 11:09:50 +1200 Subject: [PATCH 019/369] Add descriptions for finding pipeline and rule uuids for api calls --- pages/pipelines/rules/manage_rules.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pages/pipelines/rules/manage_rules.md b/pages/pipelines/rules/manage_rules.md index d93188c4f6..abd614fc3a 100644 --- a/pages/pipelines/rules/manage_rules.md +++ b/pages/pipelines/rules/manage_rules.md @@ -38,6 +38,8 @@ where: <%= render_markdown partial: 'apis/descriptions/rest_org_slug' %> +<%= render_markdown partial: 'apis/descriptions/rest_pipeline_uuid' %> + ## Using the GraphQL API To [create a new rule](/docs/apis/graphql/schemas/mutation/rulecreate) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: @@ -47,7 +49,7 @@ mutation { ruleCreate(input: { organizationId: "organization-id", name: "pipeline.trigger_build.pipeline", - value: "{\"triggering_pipeline_uuid\":\"{uuid-of-build-pipeline}\",\"triggered_pipeline_uuid\":\"{uuid-of-deploy-pipeline}\"}" + value: "{\"triggering_pipeline_uuid\":\"{uuid-of-source-pipeline}\",\"triggered_pipeline_uuid\":\"{uuid-of-target-pipeline}\"}" }) { rule { id @@ -79,6 +81,8 @@ where: <%= render_markdown partial: 'apis/descriptions/graphql_organization_id' %> +<%= render_markdown partial: 'apis/descriptions/rest_pipeline_uuid' %> + ## Delete a rule Organization admins can delete rules using the [**Rules** page in **Organization settings**](#delete-a-rule-using-the-buildkite-ui), as well as via the Buildkite [REST API](#delete-a-rule-using-the-rest-api) and [GraphQL API](#delete-a-rule-using-the-graphql-api). @@ -107,6 +111,8 @@ where: <%= render_markdown partial: 'apis/descriptions/rest_org_slug' %> +<%= render_markdown partial: 'apis/descriptions/rest_rule_uuid' %> + ## Using the GraphQL API To [delete a rule](/docs/apis/graphql/schemas/mutation/ruledelete) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: @@ -125,4 +131,5 @@ mutation { where: <%= render_markdown partial: 'apis/descriptions/graphql_organization_id' %> - + +<%= render_markdown partial: 'apis/descriptions/graphql_rule_id' %> From d2a07750634da419b43c45106ad70fcef1772846 Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Tue, 27 Aug 2024 11:10:06 +1200 Subject: [PATCH 020/369] Add descriptions for rule and pipeline uuids --- pages/apis/descriptions/_graphql_rule_id.md | 28 +++++++++++++++++++ .../apis/descriptions/_rest_pipeline_uuid.md | 24 ++++++++++++++++ pages/apis/descriptions/_rest_rule_uuid.md | 11 ++++++++ 3 files changed, 63 insertions(+) create mode 100644 pages/apis/descriptions/_graphql_rule_id.md create mode 100644 pages/apis/descriptions/_rest_pipeline_uuid.md create mode 100644 pages/apis/descriptions/_rest_rule_uuid.md diff --git a/pages/apis/descriptions/_graphql_rule_id.md b/pages/apis/descriptions/_graphql_rule_id.md new file mode 100644 index 0000000000..18f54f32e6 --- /dev/null +++ b/pages/apis/descriptions/_graphql_rule_id.md @@ -0,0 +1,28 @@ +- `ruleId` (required) can be obtained: + + * From the **Rules** section of your **Organization Settings** page, accessed by selecting **Settings** in the global navigation of your organization in Buildkite. + + * By running a [List rules](/docs/apis/graphql/cookbooks/rules#list-rules) GraphQL API query and obtaining this value from the `id` in the response associated with the rule type, source and target of the rule you wish to find (specified by the `name`, `source` and `target` values in the response). For example: + + ```graphql + query getRules { + organization(slug: "organization-slug") { + rules(first: 10) { + edges { + node { + id + name + source { + ... on Pipeline + slug + } + target { + ... on Pipeline + slug + } + } + } + } + } + } + ``` diff --git a/pages/apis/descriptions/_rest_pipeline_uuid.md b/pages/apis/descriptions/_rest_pipeline_uuid.md new file mode 100644 index 0000000000..36e13ff2a8 --- /dev/null +++ b/pages/apis/descriptions/_rest_pipeline_uuid.md @@ -0,0 +1,24 @@ +- `{pipeline.uuid}` can be obtained: + + * From the **Pipeline Settings** page of a given pipeline. To do this: + 1. Select **Pipelines** (in the global navigation) > the specific pipeline > **Settings**. + 1. Once on the **Pipeline Settings** page, copy the `UUID` value from the **GraphQL API Integration** section, which is the `{pipeline.uuid}` value. + + * By running a [Get pipeline](/docs/apis/rest-api/pipelines#get-a-pipeline) REST API query and obtaining this value from the `id` in the response. For example: + + ```bash + curl -H "Authorization: Bearer $TOKEN" \ + - X GET "https://api.buildkite.com/v2/organizations/{org.slug}/pipelines/{pipeline.slug}" + ``` + + * By running a [Get pipeline](/docs/apis/graphql/schemas/query/pipeline) GraphQL API query and obtaining this value from the `uuid` in the response. For example: + + ```graphql + query getPipeline { + organization(slug: "organization-slug") { + pipeline(slug: "pipeline-slug") { + uuid + } + } + } + ``` diff --git a/pages/apis/descriptions/_rest_rule_uuid.md b/pages/apis/descriptions/_rest_rule_uuid.md new file mode 100644 index 0000000000..bdf3175b8f --- /dev/null +++ b/pages/apis/descriptions/_rest_rule_uuid.md @@ -0,0 +1,11 @@ +- `{rule.uuid}` can be obtained: + + * From the **Rules** section of your **Organization Settings** page, accessed by selecting **Settings** in the global navigation of your organization in Buildkite. + + * By running a [List rules](/docs/apis/rest-api/rules#list-rules) REST API query and obtaining this value from the `uuid` in the response associated with the rule type, source and target of the rule you wish to find (specified by the `name`, `source` and `target` values in the response). For example: + + ```bash + curl -H "Authorization: Bearer $TOKEN" \ + - X GET "https://api.buildkite.com/v2/organizations/{org.slug}/rules" + ``` + From e3d531bbc67e5a26195f7184a4b402da86c78c64 Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Tue, 27 Aug 2024 11:10:13 +1200 Subject: [PATCH 021/369] Add cookbook for rules --- pages/apis/graphql/cookbooks/rules.md | 95 +++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 pages/apis/graphql/cookbooks/rules.md diff --git a/pages/apis/graphql/cookbooks/rules.md b/pages/apis/graphql/cookbooks/rules.md new file mode 100644 index 0000000000..954e397706 --- /dev/null +++ b/pages/apis/graphql/cookbooks/rules.md @@ -0,0 +1,95 @@ +# Rules + +A collection of common tasks with rules using the GraphQL API. + +You can test out the Buildkite GraphQL API using the [Buildkite explorer](https://graphql.buildkite.com/explorer). This includes built-in documentation under the **Docs** panel. + +## List rules + +Get the first 10 rules and their information for an organization: + +```graphql + query getRules { + organization(slug: "organization-slug") { + rules(first: 10) { + edges { + node { + id + name + targetType + sourceType + source { + ... on Pipeline + slug + } + target { + ... on Pipeline + slug + } + effect + action + createdBy { + id + name + } + } + } + } + } + } + ``` + +## Create a rule + +Create a rule: + +```graphql +mutation { + ruleCreate(input: { + organizationId: "organization-id", + name: "pipeline.trigger_build.pipeline", + value: "{\"triggering_pipeline_uuid\":\"{uuid-of-source-pipeline}\",\"triggered_pipeline_uuid\":\"{uuid-of-target-pipeline}\"}" + }) { + rule { + id + name + targetType + sourceType + source { + ... on Pipeline { + uuid + } + } + target { + ... on Pipeline { + uuid + } + } + effect + action + createdBy { + id + name + } + } + } +} +``` + +## Delete a rule + +Delete a rule: + +```graphql +mutation { + ruleDelete(input: { + organizationId: "organization-id", + id: "rule-id" + }) { + deletedRuleId + } +} +``` + + + From 45db920863390a38fb13407fb28910b193075edd Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Tue, 27 Aug 2024 11:11:32 +1200 Subject: [PATCH 022/369] Add rule cookbook to graphql nav --- data/nav_graphql.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/nav_graphql.yml b/data/nav_graphql.yml index 512ea4fd01..d5a2fe2398 100644 --- a/data/nav_graphql.yml +++ b/data/nav_graphql.yml @@ -24,6 +24,8 @@ path: apis/graphql/cookbooks/pipelines - name: Pipeline templates path: apis/graphql/cookbooks/pipeline-templates + - name: Rules + path: apis/graphql/cookbooks/rules - name: Organizations path: apis/graphql/cookbooks/organizations - name: Teams From 9c700f9ab351b66a54147a65cd2bb65631a4d44a Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Tue, 27 Aug 2024 11:16:31 +1200 Subject: [PATCH 023/369] UI -> interface --- pages/pipelines/rules/manage_rules.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/pipelines/rules/manage_rules.md b/pages/pipelines/rules/manage_rules.md index abd614fc3a..f6f7003089 100644 --- a/pages/pipelines/rules/manage_rules.md +++ b/pages/pipelines/rules/manage_rules.md @@ -4,7 +4,7 @@ Organization admins can create new rules using the [**Rules** page in **Organization settings**](#create-a-rule-using-the-buildkite-ui), as well as via the Buildkite [REST API](#create-a-rule-using-the-rest-api) and [GraphQL API](#create-a-rule-using-the-graphql-api). -### Using the Buildkite UI +### Using the Buildkite interface To create a new rule using the Buildkite UI: @@ -87,7 +87,7 @@ where: Organization admins can delete rules using the [**Rules** page in **Organization settings**](#delete-a-rule-using-the-buildkite-ui), as well as via the Buildkite [REST API](#delete-a-rule-using-the-rest-api) and [GraphQL API](#delete-a-rule-using-the-graphql-api). -### Using the Buildkite UI +### Using the Buildkite interface To delete a rule using the Buildkite UI: From bdc8bc769a93ff7c9320ae6f933a5fffa00a4739 Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Tue, 27 Aug 2024 11:26:00 +1200 Subject: [PATCH 024/369] Run bundle exec rake graphql:generate --- data/nav_graphql.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/data/nav_graphql.yml b/data/nav_graphql.yml index d5a2fe2398..512ea4fd01 100644 --- a/data/nav_graphql.yml +++ b/data/nav_graphql.yml @@ -24,8 +24,6 @@ path: apis/graphql/cookbooks/pipelines - name: Pipeline templates path: apis/graphql/cookbooks/pipeline-templates - - name: Rules - path: apis/graphql/cookbooks/rules - name: Organizations path: apis/graphql/cookbooks/organizations - name: Teams From 6cfada1ee99c139f568937242827f84b31deb763 Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Wed, 28 Aug 2024 11:24:38 +1200 Subject: [PATCH 025/369] Fix url links and typos, rename triggered/triggering to source/target, and rename name to type --- pages/apis/descriptions/_graphql_rule_id.md | 4 +- pages/apis/descriptions/_rest_rule_uuid.md | 2 +- pages/apis/graphql/cookbooks/rules.md | 14 +++--- pages/apis/rest_api/rules.md | 48 ++++++++++----------- pages/pipelines/rules/manage_rules.md | 22 +++++----- pages/pipelines/rules/overview.md | 10 ++--- 6 files changed, 50 insertions(+), 50 deletions(-) diff --git a/pages/apis/descriptions/_graphql_rule_id.md b/pages/apis/descriptions/_graphql_rule_id.md index 18f54f32e6..ea8538d60b 100644 --- a/pages/apis/descriptions/_graphql_rule_id.md +++ b/pages/apis/descriptions/_graphql_rule_id.md @@ -2,7 +2,7 @@ * From the **Rules** section of your **Organization Settings** page, accessed by selecting **Settings** in the global navigation of your organization in Buildkite. - * By running a [List rules](/docs/apis/graphql/cookbooks/rules#list-rules) GraphQL API query and obtaining this value from the `id` in the response associated with the rule type, source and target of the rule you wish to find (specified by the `name`, `source` and `target` values in the response). For example: + * By running a [List rules](/docs/apis/graphql/cookbooks/rules#list-rules) GraphQL API query and obtaining this value from the `id` in the response associated with the rule type, source and target of the rule you wish to find (specified by the `type`, `source` and `target` values in the response). For example: ```graphql query getRules { @@ -11,7 +11,7 @@ edges { node { id - name + type source { ... on Pipeline slug diff --git a/pages/apis/descriptions/_rest_rule_uuid.md b/pages/apis/descriptions/_rest_rule_uuid.md index bdf3175b8f..64b6e6b82d 100644 --- a/pages/apis/descriptions/_rest_rule_uuid.md +++ b/pages/apis/descriptions/_rest_rule_uuid.md @@ -2,7 +2,7 @@ * From the **Rules** section of your **Organization Settings** page, accessed by selecting **Settings** in the global navigation of your organization in Buildkite. - * By running a [List rules](/docs/apis/rest-api/rules#list-rules) REST API query and obtaining this value from the `uuid` in the response associated with the rule type, source and target of the rule you wish to find (specified by the `name`, `source` and `target` values in the response). For example: + * By running a [List rules](/docs/apis/rest-api/rules#rules-list-rules) REST API query and obtaining this value from the `uuid` in the response associated with the rule type, source and target of the rule you wish to find (specified by the `type`, `source` and `target` values in the response). For example: ```bash curl -H "Authorization: Bearer $TOKEN" \ diff --git a/pages/apis/graphql/cookbooks/rules.md b/pages/apis/graphql/cookbooks/rules.md index 954e397706..3a8b592270 100644 --- a/pages/apis/graphql/cookbooks/rules.md +++ b/pages/apis/graphql/cookbooks/rules.md @@ -6,7 +6,7 @@ You can test out the Buildkite GraphQL API using the [Buildkite explorer](https: ## List rules -Get the first 10 rules and their information for an organization: +Get the first 10 rules and their information for an organization. ```graphql query getRules { @@ -15,7 +15,7 @@ Get the first 10 rules and their information for an organization: edges { node { id - name + type targetType sourceType source { @@ -37,22 +37,22 @@ Get the first 10 rules and their information for an organization: } } } - ``` +``` ## Create a rule -Create a rule: +Create a rule. `value` must be a JSON encoded string. ```graphql mutation { ruleCreate(input: { organizationId: "organization-id", - name: "pipeline.trigger_build.pipeline", - value: "{\"triggering_pipeline_uuid\":\"{uuid-of-source-pipeline}\",\"triggered_pipeline_uuid\":\"{uuid-of-target-pipeline}\"}" + type: "pipeline.trigger_build.pipeline", + value: "{\"source_pipeline_uuid\":\"{uuid-of-source-pipeline}\",\"target_pipeline_uuid\":\"{uuid-of-target-pipeline}\"}" }) { rule { id - name + type targetType sourceType source { diff --git a/pages/apis/rest_api/rules.md b/pages/apis/rest_api/rules.md index 7053c8085c..9aecd618d1 100644 --- a/pages/apis/rest_api/rules.md +++ b/pages/apis/rest_api/rules.md @@ -6,7 +6,7 @@ The rules API lets you create and manage rules in your organization. [Rules](/docs/pipelines/rules/overview) allow you to manage permissions between Buildkite resources. -A rule is used to specify that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). Rules allow you to break out of the defaults provided by Buildkite such as cluster boundaries. +A rule is used to specify that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). Rules allow you to break out of the defaults provided by Buildkite such as the isolation between [clusters](/docs/clusters/overview). ### List rules @@ -20,27 +20,27 @@ curl -H "Authorization: Bearer $TOKEN" \ ```json [ { - "uuid": "42f1a7da-812d-4430-93d8-1cc7c33a6bcf", - "graphql_id": "Q2x1c3Rlci0tLTQyZjFhN2RhLTgxMmQtNDQzMC05M2Q4LTFjYzdjMzNhNmJjZg==", - "organization_uuid": "f02d6a6f-7a0e-481d-9d6d-89b427aec48d", - "url": "http://api.buildkite.com/v2/organizations/acme-inc/rules/42f1a7da-812d-4430-93d8-1cc7c33a6bcf", - "name": "pipeline.trigger_build.pipeline", - "source_type": "pipeline", - "source_uuid": "16f3b56f-4934-4546-923c-287859851332", - "target_type": "pipeline", - "target_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac", - "effect": "allow", - "action": "trigger_build", - "created_at": "2024-08-26T03:22:45.555Z", - "created_by": { + "uuid": "42f1a7da-812d-4430-93d8-1cc7c33a6bcf", + "graphql_id": "Q2x1c3Rlci0tLTQyZjFhN2RhLTgxMmQtNDQzMC05M2Q4LTFjYzdjMzNhNmJjZg==", + "organization_uuid": "f02d6a6f-7a0e-481d-9d6d-89b427aec48d", + "url": "http://api.buildkite.com/v2/organizations/acme-inc/rules/42f1a7da-812d-4430-93d8-1cc7c33a6bcf", + "type": "pipeline.trigger_build.pipeline", + "source_type": "pipeline", + "source_uuid": "16f3b56f-4934-4546-923c-287859851332", + "target_type": "pipeline", + "target_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac", + "effect": "allow", + "action": "trigger_build", + "created_at": "2024-08-26T03:22:45.555Z", + "created_by": { "id": "3d3c3bf0-7d58-4afe-8fe7-b3017d5504de", "graphql_id": "VXNlci0tLTNkM2MzYmYwLTdkNTgtNGFmZS04ZmU3LWIzMDE3ZDU1MDRkZQo=", "name": "Sam Kim", "email": "sam@example.com", "avatar_url": "https://www.gravatar.com/avatar/example", "created_at": "2013-08-29T10:10:03.000Z" - } - } + } + } ] ``` @@ -61,7 +61,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "graphql_id": "Q2x1c3Rlci0tLTQyZjFhN2RhLTgxMmQtNDQzMC05M2Q4LTFjYzdjMzNhNmJjZg==", "organization_uuid": "f02d6a6f-7a0e-481d-9d6d-89b427aec48d", "url": "http://api.buildkite.com/v2/organizations/acme-inc/rules/42f1a7da-812d-4430-93d8-1cc7c33a6bcf", - "name": "pipeline.trigger_build.pipeline", + "type": "pipeline.trigger_build.pipeline", "source_type": "pipeline", "source_uuid": "16f3b56f-4934-4546-923c-287859851332", "target_type": "pipeline", @@ -91,10 +91,10 @@ curl -H "Authorization: Bearer $TOKEN" \ -X POST "https://api.buildkite.com/v2/organizations/{org.slug}/rules" \ -H "Content-Type: application/json" \ -d '{ - "name": "pipeline.trigger_build.pipeline", + "type": "pipeline.trigger_build.pipeline", "value": { - "triggering_pipeline_uuid": "16f3b56f-4934-4546-923c-287859851332", - "triggered_pipeline_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac" + "source_pipeline_uuid": "16f3b56f-4934-4546-923c-287859851332", + "target_pipeline_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac" } }' ``` @@ -105,7 +105,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "graphql_id": "Q2x1c3Rlci0tLTQyZjFhN2RhLTgxMmQtNDQzMC05M2Q4LTFjYzdjMzNhNmJjZg==", "organization_uuid": "f02d6a6f-7a0e-481d-9d6d-89b427aec48d", "url": "http://api.buildkite.com/v2/organizations/acme-inc/rules/42f1a7da-812d-4430-93d8-1cc7c33a6bcf", - "name": "pipeline.trigger_build.pipeline", + "type": "pipeline.trigger_build.pipeline", "source_type": "pipeline", "source_uuid": "16f3b56f-4934-4546-923c-287859851332", "target_type": "pipeline", @@ -129,14 +129,14 @@ Required [request body properties](/docs/api#request-body-properties): - - + + Example:{"source_pipeline_uuid": "16f3b56f-4934-4546-923c-287859851332", "target_pipeline_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac"}
nameName of the rule. Must match one of the [available rule types](/docs/pipelines/rules/overview#available-rule-types)
+
typeThe rule type. Must match one of the [available rule types](/docs/pipelines/rules/overview#available-rule-types)
Example: "pipeline.trigger_build.pipeline"
value A hash containing the value fields for the rule.
- Example: {"triggering_pipeline_uuid": "16f3b56f-4934-4546-923c-287859851332", "triggered_pipeline_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac"}
diff --git a/pages/pipelines/rules/manage_rules.md b/pages/pipelines/rules/manage_rules.md index f6f7003089..599000eb7b 100644 --- a/pages/pipelines/rules/manage_rules.md +++ b/pages/pipelines/rules/manage_rules.md @@ -2,7 +2,7 @@ ## Create a rule -Organization admins can create new rules using the [**Rules** page in **Organization settings**](#create-a-rule-using-the-buildkite-ui), as well as via the Buildkite [REST API](#create-a-rule-using-the-rest-api) and [GraphQL API](#create-a-rule-using-the-graphql-api). +Organization admins can create new rules using the **Rules** page in **Organization settings**, as well as via the Buildkite [REST API](/docs/apis/rest-api) and [GraphQL API](/docs/apis/graphql-api). ### Using the Buildkite interface @@ -11,8 +11,8 @@ To create a new rule using the Buildkite UI: 1. Select **Settings** in the global navigation to access the **Organization settings** page. 2. Select **Rules** in the Pipelines section. 3. Select **New Rule**. -4. Under **Rule Name**, select the type of rule you want to create, such as `pipeline.trigger_build.pipeline`. -5. Under **Rule Document**, populate the relevant data. For example, if you're creating a `pipeline.trigger_build.pipeline` rule, you'll need to provide a `triggering_pipeline_uuid` and a `triggered_pipeline_uuid`. You can find the UUIDs of your pipelines on their **Settings** page under the **GraphQL API integration** section. +4. Under **Rule Type**, select the type of rule you want to create, such as `pipeline.trigger_build.pipeline`. +5. Under **Rule Document**, populate the relevant data. For example, if you're creating a `pipeline.trigger_build.pipeline` rule, you'll need to provide a `source_pipeline_uuid` and a `target_pipeline_uuid`. You can find the UUIDs of your pipelines on their **Settings** page under the **GraphQL API integration** section. 6. Select **Submit**. ### Using the REST API @@ -26,8 +26,8 @@ curl -H "Authorization: Bearer $TOKEN" \ -d '{ "rule": "pipeline.trigger_build.pipeline", "value": { - "triggering_pipeline_uuid": "{uuid-of-triggering-pipeline}", - "triggered_pipeline_uuid": "{uuid-of-triggered-pipeline}" + "source_pipeline_uuid": "{uuid-of-triggering-pipeline}", + "target_pipeline_uuid": "{uuid-of-target-pipeline}" } }' ``` @@ -40,7 +40,7 @@ where: <%= render_markdown partial: 'apis/descriptions/rest_pipeline_uuid' %> -## Using the GraphQL API +### Using the GraphQL API To [create a new rule](/docs/apis/graphql/schemas/mutation/rulecreate) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: @@ -48,12 +48,12 @@ To [create a new rule](/docs/apis/graphql/schemas/mutation/rulecreate) using the mutation { ruleCreate(input: { organizationId: "organization-id", - name: "pipeline.trigger_build.pipeline", - value: "{\"triggering_pipeline_uuid\":\"{uuid-of-source-pipeline}\",\"triggered_pipeline_uuid\":\"{uuid-of-target-pipeline}\"}" + type: "pipeline.trigger_build.pipeline", + value: "{\"source_pipeline_uuid\":\"{uuid-of-source-pipeline}\",\"target_pipeline_uuid\":\"{uuid-of-target-pipeline}\"}" }) { rule { id - name + type targetType sourceType source { @@ -85,7 +85,7 @@ where: ## Delete a rule -Organization admins can delete rules using the [**Rules** page in **Organization settings**](#delete-a-rule-using-the-buildkite-ui), as well as via the Buildkite [REST API](#delete-a-rule-using-the-rest-api) and [GraphQL API](#delete-a-rule-using-the-graphql-api). +Organization admins can delete rules using the **Rules** page in **Organization settings**, as well as via the Buildkite [REST API](/docs/apis/rest-api) and [GraphQL API](/docs/apis/graphql-api). ### Using the Buildkite interface @@ -113,7 +113,7 @@ where: <%= render_markdown partial: 'apis/descriptions/rest_rule_uuid' %> -## Using the GraphQL API +### Using the GraphQL API To [delete a rule](/docs/apis/graphql/schemas/mutation/ruledelete) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: diff --git a/pages/pipelines/rules/overview.md b/pages/pipelines/rules/overview.md index d79ec77e79..2756178640 100644 --- a/pages/pipelines/rules/overview.md +++ b/pages/pipelines/rules/overview.md @@ -4,7 +4,7 @@ Rules allow you to manage permissions between Buildkite resources. Rules express that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). -Rules are typically used in tandem with [clusters](/docs/clusters/overview) to increase security and control, where clusters set hard boundaries and rules provide exceptions. +Rules provide explicit access between resources, allowing granting or restricting access between resources that would normally be determined by the default permissions. ## Available rule types @@ -18,16 +18,16 @@ Rule document: { "rule": "pipeline.trigger_build.pipeline", "value": { - "triggering_pipeline_uuid": "{triggering-pipeline-uuid}", - "triggered_pipeline_uuid": "{triggered-pipeline-uuid}" + "source_pipeline_uuid": "{triggering-pipeline-uuid}", + "target_pipeline_uuid": "{triggered-pipeline-uuid}" } } ``` Value fields: -- `triggering_pipeline_uuid` The UUID of the pipeline that is allowed to trigger another pipeline. -- `triggered_pipeline_uuid` The UUID of the pipeline that is allowed to be triggered by the `triggering_pipeline_uuid` pipeline. +- `source_pipeline_uuid` The UUID of the pipeline that is allowed to trigger another pipeline. +- `target_pipeline_uuid` The UUID of the pipeline that is allowed to be triggered by the `source_pipeline_uuid` pipeline. #### Example use case From 8dcc18b3386ac176a9d290144043dad263834ced Mon Sep 17 00:00:00 2001 From: L Suzuki Date: Fri, 30 Aug 2024 11:25:55 +1200 Subject: [PATCH 026/369] Flesh out example use cases --- pages/pipelines/rules/overview.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pages/pipelines/rules/overview.md b/pages/pipelines/rules/overview.md index 2756178640..9fc1eb85d8 100644 --- a/pages/pipelines/rules/overview.md +++ b/pages/pipelines/rules/overview.md @@ -1,16 +1,18 @@ # Rules overview -Rules allow you to manage permissions between Buildkite resources. +Rules allow you to customize permissions between Buildkite resources. -Rules express that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). +Rules express that an action (e.g. triggering a build) is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). -Rules provide explicit access between resources, allowing granting or restricting access between resources that would normally be determined by the default permissions. +Rules are used to grant or restrict access between resources that would normally be determined by the default permissions. ## Available rule types ### `pipeline.trigger_build.pipeline` -Allows a pipeline in one cluster to trigger a pipeline in another cluster. +Allows one pipeline to trigger another. This is useful where you want to allow a pipeline to trigger a build in another cluster, or if you want to allow a public pipeline to trigger a private one. + +Note that this rule type overrides the usual [trigger step permissions checks](docs/pipelines/trigger-step#permissions) on users and teams. Rule document: @@ -29,11 +31,11 @@ Value fields: - `source_pipeline_uuid` The UUID of the pipeline that is allowed to trigger another pipeline. - `target_pipeline_uuid` The UUID of the pipeline that is allowed to be triggered by the `source_pipeline_uuid` pipeline. -#### Example use case +#### Example use case: cross-cluster pipeline triggering -Imagine you use two clusters to separate the environments necessary for building and deploying your application: a CI cluster and a CD cluster. Ordinarily, pipelines in these separate clusters are not able to trigger each other due to the isolation of clusters. +Clusters may be used to separate the environments necessary for building and deploying an application. For example, a CI pipeline in cluster A and a CD pipeline cluster B. Ordinarily, pipelines in separate clusters like this are not able to trigger builds for each other due to the strict isolation of clusters. -A `pipeline.trigger_build.pipeline` rule would allow a pipeline in the CI cluster to trigger a build for a pipeline in the CD cluster, while maintaining the separation of the CI and CD agents in their respective clusters. +A `pipeline.trigger_build.pipeline` rule would allow a trigger step in the CI pipeline in cluster A to target the CD pipeline in cluster B. This would allow deploys to be triggered upon a successful CI build, while still maintaining the separation of the CI and CD agents in their respective clusters. ### `pipeline.artifacts_read.pipeline` @@ -55,3 +57,7 @@ Value fields: - `source_pipeline_uuid` The UUID of the pipeline that is allowed to read artifacts from another pipeline. - `target_pipeline_uuid` The UUID of the pipeline that is allowed to have its artifacts read by jobs in the `source_pipeline_uuid` pipeline. + +#### Example use case: sharing assets between clusters + +By default, artifacts cannot be accessed by pipelines in separate clusters. For example, a deploy pipeline in cluster B cannot ordinarily access artifacts uploaded by a CI pipeline in cluster A. A `pipeline.artifacts_read.pipeline` rule can be used to override this. For example, frontend assets uploaded as artifacts by the CI pipeline would now be accessible to the deploy pipeline via the `buildkite-agent artifact download --build xxx` command. From e899ddb8adfa2348aff3a5ada863d15ed6e5ae39 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 30 Aug 2024 10:54:55 +1000 Subject: [PATCH 027/369] Initial path changes and redirects updates for Test Analytics to Test Engine rebrand. --- config/routes.rb | 7 +-- data/nav.yml | 50 +++++++++---------- .../android_collectors.md | 0 .../ci_environments.md | 0 .../dotnet_collectors.md | 0 .../elixir_collectors.md | 0 .../flaky_test_assignment.md | 0 .../golang_collectors.md | 0 .../importing_json.md | 0 .../importing_junit_xml.md | 0 .../javascript_collectors.md | 0 .../other_collectors.md | 0 .../permissions.md | 0 .../public_test_suites.md | 0 .../python_collectors.md | 0 .../ruby_collectors.md | 0 .../rust_collectors.md | 0 .../swift_collectors.md | 0 .../test_executions.md | 0 .../test_ownership.md | 0 .../test_splitting.md | 0 .../test_suites.md | 0 .../your_own_collectors.md | 0 23 files changed, 29 insertions(+), 28 deletions(-) rename pages/{test_analytics => test_engine}/android_collectors.md (100%) rename pages/{test_analytics => test_engine}/ci_environments.md (100%) rename pages/{test_analytics => test_engine}/dotnet_collectors.md (100%) rename pages/{test_analytics => test_engine}/elixir_collectors.md (100%) rename pages/{test_analytics => test_engine}/flaky_test_assignment.md (100%) rename pages/{test_analytics => test_engine}/golang_collectors.md (100%) rename pages/{test_analytics => test_engine}/importing_json.md (100%) rename pages/{test_analytics => test_engine}/importing_junit_xml.md (100%) rename pages/{test_analytics => test_engine}/javascript_collectors.md (100%) rename pages/{test_analytics => test_engine}/other_collectors.md (100%) rename pages/{test_analytics => test_engine}/permissions.md (100%) rename pages/{test_analytics => test_engine}/public_test_suites.md (100%) rename pages/{test_analytics => test_engine}/python_collectors.md (100%) rename pages/{test_analytics => test_engine}/ruby_collectors.md (100%) rename pages/{test_analytics => test_engine}/rust_collectors.md (100%) rename pages/{test_analytics => test_engine}/swift_collectors.md (100%) rename pages/{test_analytics => test_engine}/test_executions.md (100%) rename pages/{test_analytics => test_engine}/test_ownership.md (100%) rename pages/{test_analytics => test_engine}/test_splitting.md (100%) rename pages/{test_analytics => test_engine}/test_suites.md (100%) rename pages/{test_analytics => test_engine}/your_own_collectors.md (100%) diff --git a/config/routes.rb b/config/routes.rb index 1d262438e2..e3ca23a831 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -81,7 +81,8 @@ get "/docs/quickstart/*page", to: redirect("/docs/tutorials/%{page}") get "/docs/rest-api", to: redirect("/docs/apis/rest-api") get "/docs/rest-api/*page", to: redirect("/docs/apis/rest-api/%{page}") - get "/docs/test-analytics/js-collectors", to: redirect("/docs/test-analytics/javascript-collectors") + get "/docs/test-analytics/*page", to: redirect("/docs/test-engine/%{page}") + get "/docs/test-analytics/js-collectors", to: redirect("/docs/test-engine/javascript-collectors") get "/docs/tutorials/gitlab", to: redirect("/docs/integrations/gitlab") get "/docs/tutorials/github-enterprise", to: redirect("/docs/integrations/github-enterprise") get "/docs/tutorials/bitbucket", to: redirect("/docs/integrations/bitbucket") @@ -133,7 +134,7 @@ get "/docs/agent/upgrading", to: redirect("/docs/agent/v3/upgrading", status: 301) get "/docs/agent/upgrading-to-v3", to: redirect("/docs/agent/v3/upgrading", status: 301) get "/docs/clusters/queue-metrics", to: redirect("/docs/pipelines/cluster-queue-metrics", status: 301) - get "/docs/test-analytics/java", to: redirect("/docs/test-analytics/importing-junit-xml", status: 301) + get "/docs/test-analytics/java", to: redirect("/docs/test-engine/importing-junit-xml", status: 301) # Old docs routes that we changed around during the development of the v3 agent docs get "/docs/agent/upgrading-to-v2", to: redirect("/docs/agent/v2/upgrading-to-v2", status: 301) @@ -143,7 +144,7 @@ get "/docs/agent/v3/agent-meta-data", to: redirect("/docs/agent/v3/cli-start#setting-tags", status: 301) # Pre GA test analytics - get "/docs/test-analytics/integrations", to: redirect("/docs/test-analytics", status: 301) + get "/docs/test-analytics/integrations", to: redirect("/docs/test-engine", status: 301) # Quick Reference JSON get "/docs/quick-reference/pipelines", to: "quick_reference#pipelines", as: :pipelines_quick_reference diff --git a/data/nav.yml b/data/nav.yml index a3c678ffc9..a0ef83e442 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -391,65 +391,65 @@ - name: "Cluster queue metrics" path: "pipelines/cluster-queue-metrics" pill: "beta" -- name: "Test Analytics" - path: "test-analytics" +- name: "Test Engine" + path: "test-engine" children: - name: "Overview" - path: "test-analytics" + path: "test-engine" - name: "Getting started" start_expanded: true children: - name: "Configuring test suites" - path: "test-analytics/test-suites" + path: "test-engine/test-suites" - name: "Configuring test splitting" - path: "test-analytics/test-splitting" + path: "test-engine/test-splitting" - name: "Permissions" - path: "test-analytics/permissions" + path: "test-engine/permissions" - name: "CI environment variables" - path: "test-analytics/ci-environments" + path: "test-engine/ci-environments" - name: "Test executions" - path: "test-analytics/test-executions" + path: "test-engine/test-executions" - name: "Public test suites" - path: "test-analytics/public-test-suites" + path: "test-engine/public-test-suites" - name: "Test ownership" pill: "beta" - path: "test-analytics/test-ownership" + path: "test-engine/test-ownership" - name: "Flaky test assignment" - path: "test-analytics/flaky-test-assignment" + path: "test-engine/flaky-test-assignment" - name: "Languages" start_expanded: true children: - name: "Ruby" - path: "test-analytics/ruby-collectors" + path: "test-engine/ruby-collectors" - name: "JavaScript" - path: "test-analytics/javascript-collectors" + path: "test-engine/javascript-collectors" - name: "Swift" - path: "test-analytics/swift-collectors" + path: "test-engine/swift-collectors" - name: "Android" - path: "test-analytics/android-collectors" + path: "test-engine/android-collectors" - name: "Python" - path: "test-analytics/python-collectors" + path: "test-engine/python-collectors" - name: "Go" - path: "test-analytics/golang-collectors" + path: "test-engine/golang-collectors" - name: ".NET" - path: "test-analytics/dotnet-collectors" + path: "test-engine/dotnet-collectors" - name: "Elixir" - path: "test-analytics/elixir-collectors" + path: "test-engine/elixir-collectors" - name: "Rust" - path: "test-analytics/rust-collectors" + path: "test-engine/rust-collectors" - name: "Java" - path: "test-analytics/java" + path: "test-engine/java" - name: "Other languages" - path: "test-analytics/other-collectors" + path: "test-engine/other-collectors" - name: "References" start_expanded: true children: - name: "Importing JUnit XML" - path: "test-analytics/importing-junit-xml" + path: "test-engine/importing-junit-xml" - name: "Importing JSON" - path: "test-analytics/importing-json" + path: "test-engine/importing-json" - name: "Writing your own collectors" - path: "test-analytics/your-own-collectors" + path: "test-engine/your-own-collectors" - name: "Packages" path: "packages" children: diff --git a/pages/test_analytics/android_collectors.md b/pages/test_engine/android_collectors.md similarity index 100% rename from pages/test_analytics/android_collectors.md rename to pages/test_engine/android_collectors.md diff --git a/pages/test_analytics/ci_environments.md b/pages/test_engine/ci_environments.md similarity index 100% rename from pages/test_analytics/ci_environments.md rename to pages/test_engine/ci_environments.md diff --git a/pages/test_analytics/dotnet_collectors.md b/pages/test_engine/dotnet_collectors.md similarity index 100% rename from pages/test_analytics/dotnet_collectors.md rename to pages/test_engine/dotnet_collectors.md diff --git a/pages/test_analytics/elixir_collectors.md b/pages/test_engine/elixir_collectors.md similarity index 100% rename from pages/test_analytics/elixir_collectors.md rename to pages/test_engine/elixir_collectors.md diff --git a/pages/test_analytics/flaky_test_assignment.md b/pages/test_engine/flaky_test_assignment.md similarity index 100% rename from pages/test_analytics/flaky_test_assignment.md rename to pages/test_engine/flaky_test_assignment.md diff --git a/pages/test_analytics/golang_collectors.md b/pages/test_engine/golang_collectors.md similarity index 100% rename from pages/test_analytics/golang_collectors.md rename to pages/test_engine/golang_collectors.md diff --git a/pages/test_analytics/importing_json.md b/pages/test_engine/importing_json.md similarity index 100% rename from pages/test_analytics/importing_json.md rename to pages/test_engine/importing_json.md diff --git a/pages/test_analytics/importing_junit_xml.md b/pages/test_engine/importing_junit_xml.md similarity index 100% rename from pages/test_analytics/importing_junit_xml.md rename to pages/test_engine/importing_junit_xml.md diff --git a/pages/test_analytics/javascript_collectors.md b/pages/test_engine/javascript_collectors.md similarity index 100% rename from pages/test_analytics/javascript_collectors.md rename to pages/test_engine/javascript_collectors.md diff --git a/pages/test_analytics/other_collectors.md b/pages/test_engine/other_collectors.md similarity index 100% rename from pages/test_analytics/other_collectors.md rename to pages/test_engine/other_collectors.md diff --git a/pages/test_analytics/permissions.md b/pages/test_engine/permissions.md similarity index 100% rename from pages/test_analytics/permissions.md rename to pages/test_engine/permissions.md diff --git a/pages/test_analytics/public_test_suites.md b/pages/test_engine/public_test_suites.md similarity index 100% rename from pages/test_analytics/public_test_suites.md rename to pages/test_engine/public_test_suites.md diff --git a/pages/test_analytics/python_collectors.md b/pages/test_engine/python_collectors.md similarity index 100% rename from pages/test_analytics/python_collectors.md rename to pages/test_engine/python_collectors.md diff --git a/pages/test_analytics/ruby_collectors.md b/pages/test_engine/ruby_collectors.md similarity index 100% rename from pages/test_analytics/ruby_collectors.md rename to pages/test_engine/ruby_collectors.md diff --git a/pages/test_analytics/rust_collectors.md b/pages/test_engine/rust_collectors.md similarity index 100% rename from pages/test_analytics/rust_collectors.md rename to pages/test_engine/rust_collectors.md diff --git a/pages/test_analytics/swift_collectors.md b/pages/test_engine/swift_collectors.md similarity index 100% rename from pages/test_analytics/swift_collectors.md rename to pages/test_engine/swift_collectors.md diff --git a/pages/test_analytics/test_executions.md b/pages/test_engine/test_executions.md similarity index 100% rename from pages/test_analytics/test_executions.md rename to pages/test_engine/test_executions.md diff --git a/pages/test_analytics/test_ownership.md b/pages/test_engine/test_ownership.md similarity index 100% rename from pages/test_analytics/test_ownership.md rename to pages/test_engine/test_ownership.md diff --git a/pages/test_analytics/test_splitting.md b/pages/test_engine/test_splitting.md similarity index 100% rename from pages/test_analytics/test_splitting.md rename to pages/test_engine/test_splitting.md diff --git a/pages/test_analytics/test_suites.md b/pages/test_engine/test_suites.md similarity index 100% rename from pages/test_analytics/test_suites.md rename to pages/test_engine/test_suites.md diff --git a/pages/test_analytics/your_own_collectors.md b/pages/test_engine/your_own_collectors.md similarity index 100% rename from pages/test_analytics/your_own_collectors.md rename to pages/test_engine/your_own_collectors.md From 2bef5309017e860bc5b2af0cff2b06e909e2f038 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 30 Aug 2024 11:07:39 +1000 Subject: [PATCH 028/369] Update 'test-analytics' paths to 'test-engine'. --- .../analytics/_flaky_tests_query_strings.md | 2 +- pages/packages/permissions.md | 2 +- pages/pipelines/migrate_from_jenkins.md | 2 +- pages/team_management/permissions.md | 6 +-- pages/test_analytics.md | 52 ------------------- pages/test_engine.md | 52 +++++++++++++++++++ pages/test_engine/android_collectors.md | 8 +-- pages/test_engine/ci_environments.md | 12 ++--- pages/test_engine/dotnet_collectors.md | 6 +-- pages/test_engine/elixir_collectors.md | 6 +-- pages/test_engine/flaky_test_assignment.md | 4 +- pages/test_engine/golang_collectors.md | 2 +- pages/test_engine/importing_json.md | 12 ++--- pages/test_engine/importing_junit_xml.md | 10 ++-- pages/test_engine/javascript_collectors.md | 10 ++-- pages/test_engine/other_collectors.md | 2 +- pages/test_engine/python_collectors.md | 6 +-- pages/test_engine/ruby_collectors.md | 8 +-- pages/test_engine/rust_collectors.md | 6 +-- pages/test_engine/swift_collectors.md | 8 +-- pages/test_engine/test_ownership.md | 6 +-- pages/test_engine/test_suites.md | 2 +- 22 files changed, 112 insertions(+), 112 deletions(-) delete mode 100644 pages/test_analytics.md create mode 100644 pages/test_engine.md diff --git a/pages/apis/rest_api/analytics/_flaky_tests_query_strings.md b/pages/apis/rest_api/analytics/_flaky_tests_query_strings.md index 459a7e50e5..5b0f11972a 100644 --- a/pages/apis/rest_api/analytics/_flaky_tests_query_strings.md +++ b/pages/apis/rest_api/analytics/_flaky_tests_query_strings.md @@ -5,7 +5,7 @@ search - Returns flaky tests with a name or scope that contains the search string. Users with the Ruby test collector installed can also filter results by location. + Returns flaky tests with a name or scope that contains the search string. Users with the Ruby test collector installed can also filter results by location.

Example: ?search="User#find_email", ?search="/billing_spec"

diff --git a/pages/packages/permissions.md b/pages/packages/permissions.md index 91ecdb1b28..f440230420 100644 --- a/pages/packages/permissions.md +++ b/pages/packages/permissions.md @@ -22,7 +22,7 @@ As an organization administrator, you can access the [**Organization Settings** - Add new teams or edit existing ones in the [**Team** section](https://buildkite.com/organizations/~/teams). - * After selecting a team, you can view and administer the member-, [pipeline-](/docs/team-management/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions), [registry-](#manage-teams-and-permissions-registry-level-permissions) and [team-](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions)level settings for that team. + * After selecting a team, you can view and administer the member-, [pipeline-](/docs/team-management/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions), [registry-](#manage-teams-and-permissions-registry-level-permissions) and [team-](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions)level settings for that team. - [Enable Buildkite Packages](#enabling-buildkite-packages) for your Buildkite organization. diff --git a/pages/pipelines/migrate_from_jenkins.md b/pages/pipelines/migrate_from_jenkins.md index 4f06a4cae1..338d01d487 100644 --- a/pages/pipelines/migrate_from_jenkins.md +++ b/pages/pipelines/migrate_from_jenkins.md @@ -200,4 +200,4 @@ Remember that it may take some time to adapt to the new platform, and be prepare If you need further assistance or have any questions, please don't hesitate to reach out to [support](https://buildkite.com/support). We're here to help you use Buildkite to build your dream CI/CD workflows. -After configuring Buildkite Pipelines for your team, you could get actionable insights from the tests running in pipelines using [Test Analytics](/docs/test-analytics). +After configuring Buildkite Pipelines for your team, you could get actionable insights from the tests running in pipelines using [Test Analytics](/docs/test-engine). diff --git a/pages/team_management/permissions.md b/pages/team_management/permissions.md index 7e64b916e7..4e31bdb9ee 100644 --- a/pages/team_management/permissions.md +++ b/pages/team_management/permissions.md @@ -33,7 +33,7 @@ A user who is a _Buildkite organization administrator_ can access the [**Organiz - From the **Teams** page: * Create a new team, using the **New Team** button. - * Administer (with full control) the [team-](#manage-teams-and-permissions-team-level-permissions), [pipeline-](#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions) and [registry-](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions)level settings throughout their Buildkite organization. + * Administer (with full control) the [team-](#manage-teams-and-permissions-team-level-permissions), [pipeline-](#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions) and [registry-](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions)level settings throughout their Buildkite organization. **Note:** Registry-level settings are only available once [Buildkite Packages has been enabled](/docs/packages/permissions#enabling-buildkite-packages). @@ -60,7 +60,7 @@ A user who is a _team maintainer_ on an existing team can: * Change the permission for all users in this team on any: - [pipeline](#manage-teams-and-permissions-pipeline-level-permissions) in the team to **Full Access**, **Build & Read** or **Read Only**. - - [test suite](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions) in the team to **Full Access** or **Read Only**. + - [test suite](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions) in the team to **Full Access** or **Read Only**. - [registry](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) in the team to **Full Access**, **Read & Write** or **Read Only**. To do this, select the appropriate tab (**Pipelines**, **Test Suites** or **Package Registries**) and then select the required permission for the item, although be aware of the [caveat below](#changing-full-access-permissions-on-pipelines-test-suites-and-registries). @@ -86,7 +86,7 @@ A user who is a _team maintainer_ on an existing team can: As indicated in the Buildkite interface, a user who is in a team is known as a **Team Member**, and such users have fewer permissions within the team (that is, no team management capabilities) than a **Team Maintainer**. -All team members in a team have the same level of access to the [pipelines](#manage-teams-and-permissions-pipeline-level-permissions), [test suites](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions), and [registries](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) in the team. If you need to have more fine grained control over the pipelines, test suites or registries in a team, you can create more teams with different permissions. +All team members in a team have the same level of access to the [pipelines](#manage-teams-and-permissions-pipeline-level-permissions), [test suites](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions), and [registries](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) in the team. If you need to have more fine grained control over the pipelines, test suites or registries in a team, you can create more teams with different permissions. > 🚧 Changing **Full Access** permissions on pipelines, test suites and registries > As a team maintainer, once you change the permission on any of these items away from **Full Access**, you could lose the ability to change the permissions on that item again. This can happen if you are no longer a member of a team that provides **Full Access** to this item. diff --git a/pages/test_analytics.md b/pages/test_analytics.md deleted file mode 100644 index bde6c54a6a..0000000000 --- a/pages/test_analytics.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -template: "landing_page" ---- - -# Buildkite Test Analytics - -Where Buildkite Pipelines help you automate your build pipelines, -Test Analytics helps you track and analyze the steps in that pipeline that involve tests: - -- Ship code to production faster by optimizing test suites -- Works with any continuous integration -- Identify, fix, and monitor test suite performance -- Track, improve, and monitor test suite reliability - -<%= image "overview.png", width: 975, height: 205, alt: "Screenshot of test suite trend showing five metrics over 28 days" %> - -## Get started - - - -
- <%= button ":rspec: RSpec", "/docs/test-analytics/ruby-collectors#rspec-collector" %> - <%= button ":ruby: minitest", "/docs/test-analytics/ruby-collectors#minitest-collector" %> - <%= button ":jest: Jest", "/docs/test-analytics/javascript-collectors#configure-the-test-framework-jest" %> - <%= button ":mocha: Mocha", "/docs/test-analytics/javascript-collectors#configure-the-test-framework-mocha" %> - <%= button ":cypress: Cypress", "/docs/test-analytics/javascript-collectors#configure-the-test-framework-cypress" %> - <%= button ":jasmine: Jasmine", "/docs/test-analytics/javascript-collectors#configure-the-test-framework-jasmine" %> - <%= button ":playwright: Playwright", "/docs/test-analytics/javascript-collectors#configure-the-test-framework-playwright" %> - <%= button ":swift: Swift", "/docs/test-analytics/swift-collectors" %> - <%= button ":android: Android", "/docs/test-analytics/android-collectors" %> - <%= button ":pytest: pytest", "/docs/test-analytics/python-collectors" %> - <%= button ":golang: Go", "/docs/test-analytics/golang-collectors" %> - <%= button ":junit: JUnit", "/docs/test-analytics/importing-junit-xml" %> - <%= button ":dotnet: .NET", "/docs/test-analytics/dotnet-collectors" %> - <%= button ":elixir: Elixir", "/docs/test-analytics/elixir-collectors" %> - <%= button ":rust: Rust", "/docs/test-analytics/rust-collectors" %> -
- - - -You can also upload test results by importing [JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml). - ->📘 Data retention -> The data uploaded to Test Analytics is stored in S3 and deleted after six months. - ----- - -<%= tiles "test_analytics_features" %> - ----- - -<%= tiles "test_analytics_guides" %> diff --git a/pages/test_engine.md b/pages/test_engine.md new file mode 100644 index 0000000000..0c87b04f47 --- /dev/null +++ b/pages/test_engine.md @@ -0,0 +1,52 @@ +--- +template: "landing_page" +--- + +# Buildkite Test Analytics + +Where Buildkite Pipelines help you automate your build pipelines, +Test Analytics helps you track and analyze the steps in that pipeline that involve tests: + +- Ship code to production faster by optimizing test suites +- Works with any continuous integration +- Identify, fix, and monitor test suite performance +- Track, improve, and monitor test suite reliability + +<%= image "overview.png", width: 975, height: 205, alt: "Screenshot of test suite trend showing five metrics over 28 days" %> + +## Get started + + + +
+ <%= button ":rspec: RSpec", "/docs/test-engine/ruby-collectors#rspec-collector" %> + <%= button ":ruby: minitest", "/docs/test-engine/ruby-collectors#minitest-collector" %> + <%= button ":jest: Jest", "/docs/test-engine/javascript-collectors#configure-the-test-framework-jest" %> + <%= button ":mocha: Mocha", "/docs/test-engine/javascript-collectors#configure-the-test-framework-mocha" %> + <%= button ":cypress: Cypress", "/docs/test-engine/javascript-collectors#configure-the-test-framework-cypress" %> + <%= button ":jasmine: Jasmine", "/docs/test-engine/javascript-collectors#configure-the-test-framework-jasmine" %> + <%= button ":playwright: Playwright", "/docs/test-engine/javascript-collectors#configure-the-test-framework-playwright" %> + <%= button ":swift: Swift", "/docs/test-engine/swift-collectors" %> + <%= button ":android: Android", "/docs/test-engine/android-collectors" %> + <%= button ":pytest: pytest", "/docs/test-engine/python-collectors" %> + <%= button ":golang: Go", "/docs/test-engine/golang-collectors" %> + <%= button ":junit: JUnit", "/docs/test-engine/importing-junit-xml" %> + <%= button ":dotnet: .NET", "/docs/test-engine/dotnet-collectors" %> + <%= button ":elixir: Elixir", "/docs/test-engine/elixir-collectors" %> + <%= button ":rust: Rust", "/docs/test-engine/rust-collectors" %> +
+ + + +You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). + +>📘 Data retention +> The data uploaded to Test Analytics is stored in S3 and deleted after six months. + +---- + +<%= tiles "test_analytics_features" %> + +---- + +<%= tiles "test_analytics_guides" %> diff --git a/pages/test_engine/android_collectors.md b/pages/test_engine/android_collectors.md index 1a641cf272..4b3618a05a 100644 --- a/pages/test_engine/android_collectors.md +++ b/pages/test_engine/android_collectors.md @@ -6,13 +6,13 @@ toc: false To use Test Analytics with your Android projects use the :github: [`test-collector-android`](https://github.com/buildkite/test-collector-android) package. -You can also upload test results by importing [JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml). +You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). ## Android -Before you start, make sure your tests run with access to [CI environment variables](/docs/test-analytics/ci-environments). +Before you start, make sure your tests run with access to [CI environment variables](/docs/test-engine/ci-environments). -1. [Create a test suite](https://buildkite.com/docs/test-analytics) and copy the test suite API token. +1. [Create a test suite](https://buildkite.com/docs/test-engine) and copy the test suite API token. 1. [Securely](/docs/pipelines/security/secrets/managing) set the `BUILDKITE_ANALYTICS_TOKEN` secret on your CI to the API token from the previous step. @@ -96,7 +96,7 @@ Before you start, make sure your tests run with access to [CI environment variab Once you're done, in your Test Analytics dashboard, you'll see analytics of test executions on all branches that include this code. -If you don't see branch names, build numbers, or commit hashes in Test Analytics, then read [CI Environments](/docs/test-analytics/ci-environments) to learn more about exporting your environment to the collector. +If you don't see branch names, build numbers, or commit hashes in Test Analytics, then read [CI Environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. ### Debugging diff --git a/pages/test_engine/ci_environments.md b/pages/test_engine/ci_environments.md index eddab3d8e7..26cd4b63f4 100644 --- a/pages/test_engine/ci_environments.md +++ b/pages/test_engine/ci_environments.md @@ -4,13 +4,13 @@ Buildkite Test Analytics collectors automatically detect common continuous integ If available, test collectors gather information about your test runs, such as branch names and build IDs. Test collectors gather information from the following CI environments: -- [Buildkite](/docs/test-analytics/ci-environments#buildkite) -- [CircleCI](/docs/test-analytics/ci-environments#circleci) -- [GitHub Actions](/docs/test-analytics/ci-environments#github-actions) +- [Buildkite](/docs/test-engine/ci-environments#buildkite) +- [CircleCI](/docs/test-engine/ci-environments#circleci) +- [GitHub Actions](/docs/test-engine/ci-environments#github-actions) -If you run test collectors inside [containers](/docs/test-analytics/ci-environments#containers-and-test-collectors) or use another CI system, you must set variables to report your CI details to Buildkite. +If you run test collectors inside [containers](/docs/test-engine/ci-environments#containers-and-test-collectors) or use another CI system, you must set variables to report your CI details to Buildkite. -If you're not using a test collector, see [Importing JSON](/docs/test-analytics/importing-json) and [Importing JUnit XML](/docs/test-analytics/importing-junit-xml) to learn how to provide run environment data. +If you're not using a test collector, see [Importing JSON](/docs/test-engine/importing-json) and [Importing JUnit XML](/docs/test-engine/importing-junit-xml) to learn how to provide run environment data. ## Recommended environment variables @@ -105,7 +105,7 @@ run_env[key]=$GITHUB_ACTION-$GITHUB_RUN_NUMBER-$GITHUB_RUN_ATTEMPT If you're using other CI providers (or [containers](#containers-and-test-collectors)), then set environment variables for test collectors to gather information about your builds and tests. If you don't set these environment variables, then Test Analytics lacks the details needed to produce useful reports. -Each environment variable corresponds to a `run_env` key in the payload `https://analytics-api.buildkite.com/v1/uploads`. Read [Importing JSON](/docs/test-analytics/importing-json) to learn how these keys are used to make API calls. +Each environment variable corresponds to a `run_env` key in the payload `https://analytics-api.buildkite.com/v1/uploads`. Read [Importing JSON](/docs/test-engine/importing-json) to learn how these keys are used to make API calls. diff --git a/pages/test_engine/dotnet_collectors.md b/pages/test_engine/dotnet_collectors.md index 16aeb63f44..9067502297 100644 --- a/pages/test_engine/dotnet_collectors.md +++ b/pages/test_engine/dotnet_collectors.md @@ -6,11 +6,11 @@ toc: false To use Test Analytics with your .NET projects use the :github: [`test-collector-dotnet`](https://github.com/buildkite/test-collector-dotnet) package with xUnit. -You can also upload test results by importing [JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml). +You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). -Before you start, make sure .NET runs with access to [CI environment variables](/docs/test-analytics/ci-environments). +Before you start, make sure .NET runs with access to [CI environment variables](/docs/test-engine/ci-environments). -1. Create a [test suite](/docs/test-analytics/test-suites) and copy the API token that it gives you. +1. Create a [test suite](/docs/test-engine/test-suites) and copy the API token that it gives you. 1. Add `Buildkite.TestAnalytics.Xunit` to your list of dependencies in your xUnit test project: diff --git a/pages/test_engine/elixir_collectors.md b/pages/test_engine/elixir_collectors.md index f7ba088067..c00de0c2f0 100644 --- a/pages/test_engine/elixir_collectors.md +++ b/pages/test_engine/elixir_collectors.md @@ -6,15 +6,15 @@ toc: false To use Test Analytics with your Elixir projects use :github: [`test_collector_elixir`](https://github.com/buildkite/test_collector_elixir) with ExUnit. -You can also upload test results by importing [JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml). +You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). ## ExUnit [ExUnit](https://hexdocs.pm/ex_unit/) is a Elixir unit test library. -Before you start, make sure ExUnit runs with access to [CI environment variables](/docs/test-analytics/ci-environments). +Before you start, make sure ExUnit runs with access to [CI environment variables](/docs/test-engine/ci-environments). -1. Create a [test suite](/docs/test-analytics/test-suites) and copy the API token that it gives you. +1. Create a [test suite](/docs/test-engine/test-suites) and copy the API token that it gives you. 1. Add `buildkite_test_collector` to your list of dependencies in `mix.exs`: diff --git a/pages/test_engine/flaky_test_assignment.md b/pages/test_engine/flaky_test_assignment.md index a38135128e..77be705060 100644 --- a/pages/test_engine/flaky_test_assignment.md +++ b/pages/test_engine/flaky_test_assignment.md @@ -1,6 +1,6 @@ # Flaky test assignment -Customers on the [Pro and Enterprise plans](https://buildkite.com/pricing) can assign flaky tests to [teams](/docs/test-analytics/permissions#manage-teams-and-permissions). +Customers on the [Pro and Enterprise plans](https://buildkite.com/pricing) can assign flaky tests to [teams](/docs/test-engine/permissions#manage-teams-and-permissions). ## Enabling flaky test assignments @@ -36,6 +36,6 @@ When an assigned test has not flaked in more than 7 days, it is moved to the **O ## Weekly flaky test summary -You're able to schedule a weekly summary of the flakiest tests assigned to your teams. Visit the **Suite settings** page to create new notifications, or manage existing ones. If you would like to set up auto assignment, check out our [Test ownership](/docs/test-analytics/test-ownership) feature. +You're able to schedule a weekly summary of the flakiest tests assigned to your teams. Visit the **Suite settings** page to create new notifications, or manage existing ones. If you would like to set up auto assignment, check out our [Test ownership](/docs/test-engine/test-ownership) feature. <%= image "flaky-test-summary-mailer.png", width: 1960/2, height: 630/2, alt: "Flaky test page showing team assignments" %> diff --git a/pages/test_engine/golang_collectors.md b/pages/test_engine/golang_collectors.md index 6610ba2812..8a71a5f475 100644 --- a/pages/test_engine/golang_collectors.md +++ b/pages/test_engine/golang_collectors.md @@ -4,7 +4,7 @@ toc: false # Configuring Go with Test Analytics -To use Test Analytics with your [Go](https://go.dev/) language projects use [gotestsum](https://github.com/gotestyourself/gotestsum) to generate JUnit XML files, then [upload the JUnit XML files](/docs/test-analytics/importing-junit-xml) to Test Analytics. +To use Test Analytics with your [Go](https://go.dev/) language projects use [gotestsum](https://github.com/gotestyourself/gotestsum) to generate JUnit XML files, then [upload the JUnit XML files](/docs/test-engine/importing-junit-xml) to Test Analytics. 1. Install [gotestsum](https://github.com/gotestyourself/gotestsum): diff --git a/pages/test_engine/importing_json.md b/pages/test_engine/importing_json.md index 68e7f2ecb0..a681aa65b5 100644 --- a/pages/test_engine/importing_json.md +++ b/pages/test_engine/importing_json.md @@ -1,12 +1,12 @@ # Importing JSON -If a test collector is not available for your test framework, you can upload tests results directly to the Test Analytics API or [write your own test collector](/docs/test-analytics/your-own-collectors). -You can upload JSON-formatted test results (described in this page) or [JUnit XML](/docs/test-analytics/importing-junit-xml). +If a test collector is not available for your test framework, you can upload tests results directly to the Test Analytics API or [write your own test collector](/docs/test-engine/your-own-collectors). +You can upload JSON-formatted test results (described in this page) or [JUnit XML](/docs/test-engine/importing-junit-xml). ## How to import JSON in Buildkite -It's possible to import JSON (or [JUnit](/docs/test-analytics/importing-junit-xml#how-to-import-junit-xml-in-buildkite) files) to Buildkite Test Analytics with or without the help of a plugin. +It's possible to import JSON (or [JUnit](/docs/test-engine/importing-junit-xml#how-to-import-junit-xml-in-buildkite) files) to Buildkite Test Analytics with or without the help of a plugin. ### Using a plugin @@ -56,7 +56,7 @@ For example, to import the contents of a [JSON-formatted test results](#json-tes https://analytics-api.buildkite.com/v1/uploads ``` -To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-analytics/ci-environments#buildkite). +To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-engine/ci-environments#buildkite). A single file can have a maximum of 5000 test results, and if that limit is exceeded then the upload request will fail. To upload more than 5000 test results for a single run upload multiple smaller files with the same `run_env[key]`. @@ -85,7 +85,7 @@ For example, to import the contents of a `test-results.json` file in a CircleCI https://analytics-api.buildkite.com/v1/uploads ``` -To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-analytics/ci-environments#circleci). +To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-engine/ci-environments#circleci). A single file can have a maximum of 5000 test results, and if that limit is exceeded then the upload request will fail. To upload more than 5000 test results for a single run upload multiple smaller files with the same `run_env[key]`. @@ -114,7 +114,7 @@ For example, to import the contents of a `test-results.json` file in a GitHub Ac https://analytics-api.buildkite.com/v1/uploads ``` -To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-analytics/ci-environments#github-actions). +To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-engine/ci-environments#github-actions). A single file can have a maximum of 5000 test results, and if that limit is exceeded then the upload request will fail. To upload more than 5000 test results for a single run upload multiple smaller files with the same `run_env[key]`. diff --git a/pages/test_engine/importing_junit_xml.md b/pages/test_engine/importing_junit_xml.md index a300e03478..9709105bab 100644 --- a/pages/test_engine/importing_junit_xml.md +++ b/pages/test_engine/importing_junit_xml.md @@ -1,6 +1,6 @@ # Importing JUnit XML -While most test frameworks have a built-in JUnit XML export feature, these JUnit reports do not provide detailed span information. Therefore, features in Test Analytics that depend on span information aren't available when using JUnit as a data source. If you need span information, consider using the [JSON import](/docs/test-analytics/importing-json) API instead. +While most test frameworks have a built-in JUnit XML export feature, these JUnit reports do not provide detailed span information. Therefore, features in Test Analytics that depend on span information aren't available when using JUnit as a data source. If you need span information, consider using the [JSON import](/docs/test-engine/importing-json) API instead. ## Mandatory JUnit XML attributes @@ -15,7 +15,7 @@ To learn more about the JUnit XML file format, see [Common JUnit XML format & ex ## How to import JUnit XML in Buildkite -It's possible to import XML-formatted JUnit (or [JSON](/docs/test-analytics/importing-json#how-to-import-json-in-buildkite)) test results to Buildkite Test Analytics with or without the help of a plugin. +It's possible to import XML-formatted JUnit (or [JSON](/docs/test-engine/importing-json#how-to-import-json-in-buildkite)) test results to Buildkite Test Analytics with or without the help of a plugin. ### Using a plugin @@ -64,7 +64,7 @@ For example, to import the contents of a `junit.xml` file in a Buildkite pipelin https://analytics-api.buildkite.com/v1/uploads ``` -To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-analytics/ci-environments#buildkite). +To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-engine/ci-environments#buildkite). Note that when a payload is processed, Buildkite validates and queues each test execution result in a loop. For that reason, it is possible for some to be queued and others to be skipped. Even when some or all test executions get skipped, REST API will respond with a `202 Accepted` because the upload and the run were created in the database, but the skipped test execution results were not ingested. @@ -96,7 +96,7 @@ For example, to import the contents of a `junit.xml` file in a CircleCI pipeline https://analytics-api.buildkite.com/v1/uploads ``` -To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-analytics/ci-environments#circleci). +To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-engine/ci-environments#circleci). Note that when a payload is processed, Buildkite validates and queues each test execution result in a loop. For that reason, it is possible for some to be queued and others to be skipped. Even when some or all test executions get skipped, REST API will respond with a `202 Accepted` because the upload and the run were created in the database, but the skipped test execution results were not ingested. @@ -129,7 +129,7 @@ For example, to import the contents of a `junit.xml` file in a GitHub Actions pi https://analytics-api.buildkite.com/v1/uploads ``` -To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-analytics/ci-environments). +To learn more about passing through environment variables to `run_env`-prefixed fields, see [CI environments](/docs/test-engine/ci-environments). Note that when a payload is processed, Buildkite validates and queues each test execution result in a loop. For that reason, it is possible for some to be queued and others to be skipped. Even when some or all test executions get skipped, REST API will respond with a `202 Accepted` because the upload and the run were created in the database, but the skipped test execution results were not ingested. diff --git a/pages/test_engine/javascript_collectors.md b/pages/test_engine/javascript_collectors.md index 3b394eaca4..3227401d70 100644 --- a/pages/test_engine/javascript_collectors.md +++ b/pages/test_engine/javascript_collectors.md @@ -8,7 +8,7 @@ To use Test Analytics with your JavaScript (npm) projects, use the :github: [`te - [Cypress](https://www.cypress.io) - [Playwright](https://playwright.dev) -You can also upload test results by importing [JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml). +You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). ## Add the test collector package @@ -50,7 +50,7 @@ If you're already using Jest, you can add `buildkite-test-collector/jest/reporte To configure Jest: -1. Make sure Jest runs with access to [CI environment variables](/docs/test-analytics/ci-environments). +1. Make sure Jest runs with access to [CI environment variables](/docs/test-engine/ci-environments). 1. Add `"buildkite-test-collector/jest/reporter"` to [Jest's `reporters` configuration array](https://jestjs.io/docs/configuration#reporters-arraymodulename--modulename-options) (typically found in `jest.config.js`, `jest.config.js`, or `package.json`): ```json @@ -127,7 +127,7 @@ To configure Mocha: ### Cypress To configure Cypress: -1. Make sure Cypress runs with access to [CI environment variables](/docs/test-analytics/ci-environments). +1. Make sure Cypress runs with access to [CI environment variables](/docs/test-engine/ci-environments). 1. Update your [Cypress configuration](https://docs.cypress.io/guides/references/configuration). ```js @@ -154,7 +154,7 @@ If you're already using Playwright, you can add `buildkite-test-collector/playwr To configure Playwright: -1. Make sure Playwright runs with access to [CI environment variables](/docs/test-analytics/ci-environments). +1. Make sure Playwright runs with access to [CI environment variables](/docs/test-engine/ci-environments). 1. Add `"buildkite-test-collector/playwright/reporter"` to [Playwright's `reporter` configuration array](https://playwright.dev/docs/test-reporters#multiple-reporters) (typically found in `playwright.config.js`): ```js @@ -193,7 +193,7 @@ When your collector is installed, commit and push your changes: After completing these steps, you'll see the analytics of test executions on all branches that include this code in the Test Analytics dashboard. -If you don't see branch names, build numbers, or commit hashes in the Test Analytics dashboard, see [CI environments](/docs/test-analytics/ci-environments) to learn more about exporting your environment. +If you don't see branch names, build numbers, or commit hashes in the Test Analytics dashboard, see [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment. ## Troubleshooting missing test executions and --forceExit diff --git a/pages/test_engine/other_collectors.md b/pages/test_engine/other_collectors.md index c19815ac59..54a371f7ff 100644 --- a/pages/test_engine/other_collectors.md +++ b/pages/test_engine/other_collectors.md @@ -4,4 +4,4 @@ toc: false # Collecting data from other test frameworks -You can integrate any language and framework by uploading [Test Analytics JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml) after your tests run. You can also [build your own collector](/docs/test-analytics/your-own-collectors). +You can integrate any language and framework by uploading [Test Analytics JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml) after your tests run. You can also [build your own collector](/docs/test-engine/your-own-collectors). diff --git a/pages/test_engine/python_collectors.md b/pages/test_engine/python_collectors.md index 0d16201413..5fba392136 100644 --- a/pages/test_engine/python_collectors.md +++ b/pages/test_engine/python_collectors.md @@ -6,14 +6,14 @@ toc: false To use Test Analytics with your Python projects use the [`buildkite-test-collector`](https://pypi.org/project/buildkite-test-collector/) package with pytest. -You can also upload test results by importing [JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml). +You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). ## pytest collector pytest is a testing framework for Python. If you're already using pytest, then you can install `buildkite-test-collector` to collect test results into your Test Analytics dashboard. -Before you start, make sure pytest runs with access to [CI environment variables](/docs/test-analytics/ci-environments). +Before you start, make sure pytest runs with access to [CI environment variables](/docs/test-engine/ci-environments). To get started with `buildkite-test-collector`: @@ -54,4 +54,4 @@ To get started with `buildkite-test-collector`: Once you're done, in your Test Analytics dashboard, you'll see analytics of test executions on all branches that include this code. -If you don't see branch names, build numbers, or commit hashes in Test Analytics, then read [CI environments](/docs/test-analytics/ci-environments) to learn more about exporting your environment to the collector. +If you don't see branch names, build numbers, or commit hashes in Test Analytics, then read [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. diff --git a/pages/test_engine/ruby_collectors.md b/pages/test_engine/ruby_collectors.md index dba2692bee..8c67f82db0 100644 --- a/pages/test_engine/ruby_collectors.md +++ b/pages/test_engine/ruby_collectors.md @@ -2,7 +2,7 @@ To use Test Analytics with your [Ruby](https://www.ruby-lang.org/) projects use the :github: [`test-collectors-ruby`](https://github.com/buildkite/test-collector-ruby) gem with RSpec or minitest. -You can also upload test results by importing [JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml). +You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). ## RSpec collector @@ -10,7 +10,7 @@ You can also upload test results by importing [JSON](/docs/test-analytics/import [RSpec](https://rspec.info/) is a behavior-driven development library for Ruby. If you're already using RSpec for your tests, add the `buildkite-test_collector` gem to your code to collect your test results into your Test Analytics dashboard. -Before you start, make sure RSpec runs with access to [CI environment variables](/docs/test-analytics/ci-environments). +Before you start, make sure RSpec runs with access to [CI environment variables](/docs/test-engine/ci-environments). 1. Create a new branch: @@ -50,7 +50,7 @@ Before you start, make sure RSpec runs with access to [CI environment variables] Once you're done, in your Test Analytics dashboard, you'll see analytics of test executions on all branches that include this code. -If you don't see branch names, build numbers, or commit hashes in the Test Analytics UI, then see [CI environments](/docs/test-analytics/ci-environments) to learn more about exporting your environment to the collector. +If you don't see branch names, build numbers, or commit hashes in the Test Analytics UI, then see [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. ### Troubleshooting allow_any_instance_of errors @@ -115,7 +115,7 @@ If you're already using minitest for your tests, add the `buildkite-test_collect Once you're done, in your Test Analytics dashboard, you'll see analytics of test executions on all branches that include this code. -If you don't see branch names, build numbers, or commit hashes in the Test Analytics UI, then see [CI environments](/docs/test-analytics/ci-environments) to learn more about exporting your environment to the minitest collector. +If you don't see branch names, build numbers, or commit hashes in the Test Analytics UI, then see [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the minitest collector. ## Adding annotation spans diff --git a/pages/test_engine/rust_collectors.md b/pages/test_engine/rust_collectors.md index 399267316f..3b1f4117e5 100644 --- a/pages/test_engine/rust_collectors.md +++ b/pages/test_engine/rust_collectors.md @@ -6,11 +6,11 @@ toc: false To use Test Analytics with your [Rust](https://www.rust-lang.org/) projects use the :github: [`test-collector-rust`](https://github.com/buildkite/test-collector-rust) package with `cargo test`. -You can also upload test results by importing [JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml). +You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). -Before you start, make sure Rust runs with access to [CI environment variables](/docs/test-analytics/ci-environments). +Before you start, make sure Rust runs with access to [CI environment variables](/docs/test-engine/ci-environments). -1. Create a [test suite](/docs/test-analytics/test-suites) and copy the API token that it gives you. +1. Create a [test suite](/docs/test-engine/test-suites) and copy the API token that it gives you. 1. Install the `buildkite-test-collector` crate: diff --git a/pages/test_engine/swift_collectors.md b/pages/test_engine/swift_collectors.md index 2515cd6122..dfa125e647 100644 --- a/pages/test_engine/swift_collectors.md +++ b/pages/test_engine/swift_collectors.md @@ -6,15 +6,15 @@ toc: false To use Test Analytics with your Swift projects use the :github: [`test-collector-swift`](https://github.com/buildkite/test-collector-swift) package with XCTest. -You can also upload test results by importing [JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml). +You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). ## XCTest [XCTest](https://developer.apple.com/documentation/xctest) is a test framework to write unit tests for your Xcode projects. -Before you start, make sure XCTest runs with access to [CI environment variables](/docs/test-analytics/ci-environments). +Before you start, make sure XCTest runs with access to [CI environment variables](/docs/test-engine/ci-environments). -1. [Create a test suite](https://buildkite.com/docs/test-analytics) and copy the test suite API token. +1. [Create a test suite](https://buildkite.com/docs/test-engine) and copy the test suite API token. 1. [Securely](/docs/pipelines/security/secrets/managing) set the `BUILDKITE_ANALYTICS_TOKEN` secret on your CI to the API token from the previous step. @@ -56,7 +56,7 @@ Before you start, make sure XCTest runs with access to [CI environment variables Once you're done, in your Test Analytics dashboard, you'll see analytics of test executions on all branches that include this code. -If you don't see branch names, build numbers, or commit hashes in Test Analytics, then read [CI environments](/docs/test-analytics/ci-environments) to learn more about exporting your environment to the collector. +If you don't see branch names, build numbers, or commit hashes in Test Analytics, then read [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. ### Debugging diff --git a/pages/test_engine/test_ownership.md b/pages/test_engine/test_ownership.md index e9c334b934..006cd751f2 100644 --- a/pages/test_engine/test_ownership.md +++ b/pages/test_engine/test_ownership.md @@ -2,9 +2,9 @@ Test ownership is critical in adopting a healthy testing culture at your organization. Defining one or more teams as test owners allows these teams to become accountable for maintaining a fast and reliable test suite, ensuring confidence when you deploy your code. -Customers on the [Pro and Enterprise plans](https://buildkite.com/pricing) can assign test ownership to [teams](/docs/test-analytics/permissions#manage-teams-and-permissions). +Customers on the [Pro and Enterprise plans](https://buildkite.com/pricing) can assign test ownership to [teams](/docs/test-engine/permissions#manage-teams-and-permissions). -Test ownership is managed via team assignments in a TESTOWNERS file. The team that is the default owner of a test [will be automatically assigned flaky tests](/docs/test-analytics/flaky-test-assignment) to triage. +Test ownership is managed via team assignments in a TESTOWNERS file. The team that is the default owner of a test [will be automatically assigned flaky tests](/docs/test-engine/flaky-test-assignment) to triage. > 🚧 Buildkite test ownership is currently in private beta > Please reach out to our support team to register for early access. @@ -88,7 +88,7 @@ pipelines/ pipelines ### Permission requirements -The teams listed in your TESTOWNERS file must have [permission to access the test suite](/docs/test-analytics/permissions#manage-teams-and-permissions-test-suite-level-permissions) _before_ ownership records are created. +The teams listed in your TESTOWNERS file must have [permission to access the test suite](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions) _before_ ownership records are created. ## Setting test ownership diff --git a/pages/test_engine/test_suites.md b/pages/test_engine/test_suites.md index 3e46cefca5..56eeb8916b 100644 --- a/pages/test_engine/test_suites.md +++ b/pages/test_engine/test_suites.md @@ -12,7 +12,7 @@ To delete a suite, or regenerate its API token, go to suite settings. Test Analytics works even when your test runs are split across different agents by de-duplicating against the Test Analytics API token and unique build identifier. -The information that serves as a unique build identifier differs between CI environments. For details, see `run_env[key]` environment variables on our [CI environments page](/docs/test-analytics/ci-environments). +The information that serves as a unique build identifier differs between CI environments. For details, see `run_env[key]` environment variables on our [CI environments page](/docs/test-engine/ci-environments). ## Compare across branches From 087fe94de38ecac6e57dab2dd31923bfe33a4b74 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 30 Aug 2024 11:11:56 +1000 Subject: [PATCH 029/369] Update images path for Test Analytics to Test Engine rebranding. --- config/routes.rb | 2 +- .../flaky_test_assignment/flaky-test-no-teams.png | Bin .../flaky-test-summary-mailer.png | Bin .../flaky_test_assignment/flaky-test-teams.png | Bin .../flaky_test_assignment/outdated-assignments.png | Bin .../flaky_test_assignment/recent-assignments.png | Bin .../flaky_test_assignment/team-settings.png | Bin .../icon-find-flaky-tests.svg | 0 .../icon-monitoring.svg | 0 .../icon-performance-analysis.svg | 0 .../monitors/monitors.png | Bin .../{test_analytics => test_engine}/overview.png | Bin .../permissions/team-section-list.png | Bin .../permissions/team-section-test-suites-list.png | Bin .../permissions/user-section-teams-list.png | Bin .../public_test_suites/security.png | Bin .../public_test_suites/settings.png | Bin .../ruby_collectors/annotation-span.png | Bin .../ruby_collectors/execution-prefix-suffix.png | Bin .../test_executions/test_executions.png | Bin .../test_ownership/test-ownership.png | Bin .../test_suites/execution-issues.png | Bin .../test_suites/run-issues.png | Bin .../test_suites/span-timeline.png | Bin .../test_suites/test-execution-stats.png | Bin .../test_suites/test-stats.png | Bin .../test_suites/test-trendx.png | Bin 27 files changed, 1 insertion(+), 1 deletion(-) rename images/docs/{test_analytics => test_engine}/flaky_test_assignment/flaky-test-no-teams.png (100%) rename images/docs/{test_analytics => test_engine}/flaky_test_assignment/flaky-test-summary-mailer.png (100%) rename images/docs/{test_analytics => test_engine}/flaky_test_assignment/flaky-test-teams.png (100%) rename images/docs/{test_analytics => test_engine}/flaky_test_assignment/outdated-assignments.png (100%) rename images/docs/{test_analytics => test_engine}/flaky_test_assignment/recent-assignments.png (100%) rename images/docs/{test_analytics => test_engine}/flaky_test_assignment/team-settings.png (100%) rename images/docs/{test_analytics => test_engine}/icon-find-flaky-tests.svg (100%) rename images/docs/{test_analytics => test_engine}/icon-monitoring.svg (100%) rename images/docs/{test_analytics => test_engine}/icon-performance-analysis.svg (100%) rename images/docs/{test_analytics => test_engine}/monitors/monitors.png (100%) rename images/docs/{test_analytics => test_engine}/overview.png (100%) rename images/docs/{test_analytics => test_engine}/permissions/team-section-list.png (100%) rename images/docs/{test_analytics => test_engine}/permissions/team-section-test-suites-list.png (100%) rename images/docs/{test_analytics => test_engine}/permissions/user-section-teams-list.png (100%) rename images/docs/{test_analytics => test_engine}/public_test_suites/security.png (100%) rename images/docs/{test_analytics => test_engine}/public_test_suites/settings.png (100%) rename images/docs/{test_analytics => test_engine}/ruby_collectors/annotation-span.png (100%) rename images/docs/{test_analytics => test_engine}/ruby_collectors/execution-prefix-suffix.png (100%) rename images/docs/{test_analytics => test_engine}/test_executions/test_executions.png (100%) rename images/docs/{test_analytics => test_engine}/test_ownership/test-ownership.png (100%) rename images/docs/{test_analytics => test_engine}/test_suites/execution-issues.png (100%) rename images/docs/{test_analytics => test_engine}/test_suites/run-issues.png (100%) rename images/docs/{test_analytics => test_engine}/test_suites/span-timeline.png (100%) rename images/docs/{test_analytics => test_engine}/test_suites/test-execution-stats.png (100%) rename images/docs/{test_analytics => test_engine}/test_suites/test-stats.png (100%) rename images/docs/{test_analytics => test_engine}/test_suites/test-trendx.png (100%) diff --git a/config/routes.rb b/config/routes.rb index e3ca23a831..ddc9ff9a4e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -134,7 +134,7 @@ get "/docs/agent/upgrading", to: redirect("/docs/agent/v3/upgrading", status: 301) get "/docs/agent/upgrading-to-v3", to: redirect("/docs/agent/v3/upgrading", status: 301) get "/docs/clusters/queue-metrics", to: redirect("/docs/pipelines/cluster-queue-metrics", status: 301) - get "/docs/test-analytics/java", to: redirect("/docs/test-engine/importing-junit-xml", status: 301) + get "/docs/test-engine/java", to: redirect("/docs/test-engine/importing-junit-xml", status: 301) # Old docs routes that we changed around during the development of the v3 agent docs get "/docs/agent/upgrading-to-v2", to: redirect("/docs/agent/v2/upgrading-to-v2", status: 301) diff --git a/images/docs/test_analytics/flaky_test_assignment/flaky-test-no-teams.png b/images/docs/test_engine/flaky_test_assignment/flaky-test-no-teams.png similarity index 100% rename from images/docs/test_analytics/flaky_test_assignment/flaky-test-no-teams.png rename to images/docs/test_engine/flaky_test_assignment/flaky-test-no-teams.png diff --git a/images/docs/test_analytics/flaky_test_assignment/flaky-test-summary-mailer.png b/images/docs/test_engine/flaky_test_assignment/flaky-test-summary-mailer.png similarity index 100% rename from images/docs/test_analytics/flaky_test_assignment/flaky-test-summary-mailer.png rename to images/docs/test_engine/flaky_test_assignment/flaky-test-summary-mailer.png diff --git a/images/docs/test_analytics/flaky_test_assignment/flaky-test-teams.png b/images/docs/test_engine/flaky_test_assignment/flaky-test-teams.png similarity index 100% rename from images/docs/test_analytics/flaky_test_assignment/flaky-test-teams.png rename to images/docs/test_engine/flaky_test_assignment/flaky-test-teams.png diff --git a/images/docs/test_analytics/flaky_test_assignment/outdated-assignments.png b/images/docs/test_engine/flaky_test_assignment/outdated-assignments.png similarity index 100% rename from images/docs/test_analytics/flaky_test_assignment/outdated-assignments.png rename to images/docs/test_engine/flaky_test_assignment/outdated-assignments.png diff --git a/images/docs/test_analytics/flaky_test_assignment/recent-assignments.png b/images/docs/test_engine/flaky_test_assignment/recent-assignments.png similarity index 100% rename from images/docs/test_analytics/flaky_test_assignment/recent-assignments.png rename to images/docs/test_engine/flaky_test_assignment/recent-assignments.png diff --git a/images/docs/test_analytics/flaky_test_assignment/team-settings.png b/images/docs/test_engine/flaky_test_assignment/team-settings.png similarity index 100% rename from images/docs/test_analytics/flaky_test_assignment/team-settings.png rename to images/docs/test_engine/flaky_test_assignment/team-settings.png diff --git a/images/docs/test_analytics/icon-find-flaky-tests.svg b/images/docs/test_engine/icon-find-flaky-tests.svg similarity index 100% rename from images/docs/test_analytics/icon-find-flaky-tests.svg rename to images/docs/test_engine/icon-find-flaky-tests.svg diff --git a/images/docs/test_analytics/icon-monitoring.svg b/images/docs/test_engine/icon-monitoring.svg similarity index 100% rename from images/docs/test_analytics/icon-monitoring.svg rename to images/docs/test_engine/icon-monitoring.svg diff --git a/images/docs/test_analytics/icon-performance-analysis.svg b/images/docs/test_engine/icon-performance-analysis.svg similarity index 100% rename from images/docs/test_analytics/icon-performance-analysis.svg rename to images/docs/test_engine/icon-performance-analysis.svg diff --git a/images/docs/test_analytics/monitors/monitors.png b/images/docs/test_engine/monitors/monitors.png similarity index 100% rename from images/docs/test_analytics/monitors/monitors.png rename to images/docs/test_engine/monitors/monitors.png diff --git a/images/docs/test_analytics/overview.png b/images/docs/test_engine/overview.png similarity index 100% rename from images/docs/test_analytics/overview.png rename to images/docs/test_engine/overview.png diff --git a/images/docs/test_analytics/permissions/team-section-list.png b/images/docs/test_engine/permissions/team-section-list.png similarity index 100% rename from images/docs/test_analytics/permissions/team-section-list.png rename to images/docs/test_engine/permissions/team-section-list.png diff --git a/images/docs/test_analytics/permissions/team-section-test-suites-list.png b/images/docs/test_engine/permissions/team-section-test-suites-list.png similarity index 100% rename from images/docs/test_analytics/permissions/team-section-test-suites-list.png rename to images/docs/test_engine/permissions/team-section-test-suites-list.png diff --git a/images/docs/test_analytics/permissions/user-section-teams-list.png b/images/docs/test_engine/permissions/user-section-teams-list.png similarity index 100% rename from images/docs/test_analytics/permissions/user-section-teams-list.png rename to images/docs/test_engine/permissions/user-section-teams-list.png diff --git a/images/docs/test_analytics/public_test_suites/security.png b/images/docs/test_engine/public_test_suites/security.png similarity index 100% rename from images/docs/test_analytics/public_test_suites/security.png rename to images/docs/test_engine/public_test_suites/security.png diff --git a/images/docs/test_analytics/public_test_suites/settings.png b/images/docs/test_engine/public_test_suites/settings.png similarity index 100% rename from images/docs/test_analytics/public_test_suites/settings.png rename to images/docs/test_engine/public_test_suites/settings.png diff --git a/images/docs/test_analytics/ruby_collectors/annotation-span.png b/images/docs/test_engine/ruby_collectors/annotation-span.png similarity index 100% rename from images/docs/test_analytics/ruby_collectors/annotation-span.png rename to images/docs/test_engine/ruby_collectors/annotation-span.png diff --git a/images/docs/test_analytics/ruby_collectors/execution-prefix-suffix.png b/images/docs/test_engine/ruby_collectors/execution-prefix-suffix.png similarity index 100% rename from images/docs/test_analytics/ruby_collectors/execution-prefix-suffix.png rename to images/docs/test_engine/ruby_collectors/execution-prefix-suffix.png diff --git a/images/docs/test_analytics/test_executions/test_executions.png b/images/docs/test_engine/test_executions/test_executions.png similarity index 100% rename from images/docs/test_analytics/test_executions/test_executions.png rename to images/docs/test_engine/test_executions/test_executions.png diff --git a/images/docs/test_analytics/test_ownership/test-ownership.png b/images/docs/test_engine/test_ownership/test-ownership.png similarity index 100% rename from images/docs/test_analytics/test_ownership/test-ownership.png rename to images/docs/test_engine/test_ownership/test-ownership.png diff --git a/images/docs/test_analytics/test_suites/execution-issues.png b/images/docs/test_engine/test_suites/execution-issues.png similarity index 100% rename from images/docs/test_analytics/test_suites/execution-issues.png rename to images/docs/test_engine/test_suites/execution-issues.png diff --git a/images/docs/test_analytics/test_suites/run-issues.png b/images/docs/test_engine/test_suites/run-issues.png similarity index 100% rename from images/docs/test_analytics/test_suites/run-issues.png rename to images/docs/test_engine/test_suites/run-issues.png diff --git a/images/docs/test_analytics/test_suites/span-timeline.png b/images/docs/test_engine/test_suites/span-timeline.png similarity index 100% rename from images/docs/test_analytics/test_suites/span-timeline.png rename to images/docs/test_engine/test_suites/span-timeline.png diff --git a/images/docs/test_analytics/test_suites/test-execution-stats.png b/images/docs/test_engine/test_suites/test-execution-stats.png similarity index 100% rename from images/docs/test_analytics/test_suites/test-execution-stats.png rename to images/docs/test_engine/test_suites/test-execution-stats.png diff --git a/images/docs/test_analytics/test_suites/test-stats.png b/images/docs/test_engine/test_suites/test-stats.png similarity index 100% rename from images/docs/test_analytics/test_suites/test-stats.png rename to images/docs/test_engine/test_suites/test-stats.png diff --git a/images/docs/test_analytics/test_suites/test-trendx.png b/images/docs/test_engine/test_suites/test-trendx.png similarity index 100% rename from images/docs/test_analytics/test_suites/test-trendx.png rename to images/docs/test_engine/test_suites/test-trendx.png From cd0d595f259be13dd3ffe6be0987f2e98f81bf47 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 30 Aug 2024 16:11:36 +1000 Subject: [PATCH 030/369] Fix broken links, renumber steps, and remove formatting from headings. --- pages/pipelines/rules/manage_rules.md | 15 ++++++++++----- pages/pipelines/rules/overview.md | 8 ++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/pages/pipelines/rules/manage_rules.md b/pages/pipelines/rules/manage_rules.md index 599000eb7b..ffc63e9909 100644 --- a/pages/pipelines/rules/manage_rules.md +++ b/pages/pipelines/rules/manage_rules.md @@ -9,11 +9,16 @@ Organization admins can create new rules using the **Rules** page in **Organizat To create a new rule using the Buildkite UI: 1. Select **Settings** in the global navigation to access the **Organization settings** page. -2. Select **Rules** in the Pipelines section. -3. Select **New Rule**. -4. Under **Rule Type**, select the type of rule you want to create, such as `pipeline.trigger_build.pipeline`. -5. Under **Rule Document**, populate the relevant data. For example, if you're creating a `pipeline.trigger_build.pipeline` rule, you'll need to provide a `source_pipeline_uuid` and a `target_pipeline_uuid`. You can find the UUIDs of your pipelines on their **Settings** page under the **GraphQL API integration** section. -6. Select **Submit**. + +1. Select **Rules** in the Pipelines section. + +1. Select **New Rule**. + +1. Under **Rule Type**, select the type of rule you want to create, such as `pipeline.trigger_build.pipeline`. + +1. Under **Rule Document**, populate the relevant data. For example, if you're creating a `pipeline.trigger_build.pipeline` rule, you'll need to provide a `source_pipeline_uuid` and a `target_pipeline_uuid`. You can find the UUIDs of your pipelines on their **Settings** page under the **GraphQL API integration** section. + +1. Select **Submit**. ### Using the REST API diff --git a/pages/pipelines/rules/overview.md b/pages/pipelines/rules/overview.md index 9fc1eb85d8..38a4813cdf 100644 --- a/pages/pipelines/rules/overview.md +++ b/pages/pipelines/rules/overview.md @@ -2,17 +2,17 @@ Rules allow you to customize permissions between Buildkite resources. -Rules express that an action (e.g. triggering a build) is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). +Rules express that an action (for example, triggering a build) is allowed between a source resource (for example, a pipeline) and a target resource (for example, another pipeline). Rules are used to grant or restrict access between resources that would normally be determined by the default permissions. ## Available rule types -### `pipeline.trigger_build.pipeline` +### pipeline.trigger_build.pipeline Allows one pipeline to trigger another. This is useful where you want to allow a pipeline to trigger a build in another cluster, or if you want to allow a public pipeline to trigger a private one. -Note that this rule type overrides the usual [trigger step permissions checks](docs/pipelines/trigger-step#permissions) on users and teams. +Note that this rule type overrides the usual [trigger step permissions checks](/docs/pipelines/trigger-step#permissions) on users and teams. Rule document: @@ -37,7 +37,7 @@ Clusters may be used to separate the environments necessary for building and dep A `pipeline.trigger_build.pipeline` rule would allow a trigger step in the CI pipeline in cluster A to target the CD pipeline in cluster B. This would allow deploys to be triggered upon a successful CI build, while still maintaining the separation of the CI and CD agents in their respective clusters. -### `pipeline.artifacts_read.pipeline` +### pipeline.artifacts_read.pipeline Allows a source pipeline in one cluster to read artifacts from a target pipeline in another cluster. From fb19e4603b877fd2503349b868ee46cada92d17e Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 30 Aug 2024 17:10:04 +1000 Subject: [PATCH 031/369] Add Rules entry for GraphQL Cookbook and fix more linting issues. --- pages/apis/descriptions/_rest_rule_uuid.md | 1 - scripts/graphql_api_content/nav_data.rb | 4 ++++ vale/styles/Buildkite/h1-h6_sentence_case.yml | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pages/apis/descriptions/_rest_rule_uuid.md b/pages/apis/descriptions/_rest_rule_uuid.md index 64b6e6b82d..34d5fcde60 100644 --- a/pages/apis/descriptions/_rest_rule_uuid.md +++ b/pages/apis/descriptions/_rest_rule_uuid.md @@ -8,4 +8,3 @@ curl -H "Authorization: Bearer $TOKEN" \ - X GET "https://api.buildkite.com/v2/organizations/{org.slug}/rules" ``` - diff --git a/scripts/graphql_api_content/nav_data.rb b/scripts/graphql_api_content/nav_data.rb index 50728f0b8d..08dcb7ffdb 100644 --- a/scripts/graphql_api_content/nav_data.rb +++ b/scripts/graphql_api_content/nav_data.rb @@ -63,6 +63,10 @@ def generate_graphql_nav_data(type_sets) "name" => "Pipeline templates", "path" => "apis/graphql/cookbooks/pipeline-templates" }, + { + "name" => "Rules", + "path" => "apis/graphql/cookbooks/rules" + }, { "name" => "Organizations", "path" => "apis/graphql/cookbooks/organizations" diff --git a/vale/styles/Buildkite/h1-h6_sentence_case.yml b/vale/styles/Buildkite/h1-h6_sentence_case.yml index 525aa59e69..7b5397abe1 100644 --- a/vale/styles/Buildkite/h1-h6_sentence_case.yml +++ b/vale/styles/Buildkite/h1-h6_sentence_case.yml @@ -128,6 +128,8 @@ exceptions: - OS X - PagerDuty - Phabricator + - pipeline.artifacts_read.pipeline + - pipeline.trigger_build.pipeline - pipeline.yml - PowerShell - pytest From 66b105d6f122a9439349f4ae88ead123744a8cb6 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 30 Aug 2024 17:20:25 +1000 Subject: [PATCH 032/369] Remove extra line endings from end of Rules GraphQL page. --- pages/apis/graphql/cookbooks/rules.md | 3 --- pages/apis/graphql/graphql_cookbook.md | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pages/apis/graphql/cookbooks/rules.md b/pages/apis/graphql/cookbooks/rules.md index 3a8b592270..7cb7e328f1 100644 --- a/pages/apis/graphql/cookbooks/rules.md +++ b/pages/apis/graphql/cookbooks/rules.md @@ -90,6 +90,3 @@ mutation { } } ``` - - - diff --git a/pages/apis/graphql/graphql_cookbook.md b/pages/apis/graphql/graphql_cookbook.md index b41f6f68e8..c601cea2c5 100644 --- a/pages/apis/graphql/graphql_cookbook.md +++ b/pages/apis/graphql/graphql_cookbook.md @@ -15,5 +15,6 @@ There are recipes for a range of different topics, including: - [Packages](/docs/apis/graphql/cookbooks/packages) - [Pipelines](/docs/apis/graphql/cookbooks/pipelines) - [Pipeline templates](/docs/apis/graphql/cookbooks/pipeline-templates) +- [] - [Organizations](/docs/apis/graphql/cookbooks/organizations) - [Teams](/docs/apis/graphql/cookbooks/teams) From 5bfacf778682be5f59f5468f8738f6b88ad8db9e Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 30 Aug 2024 17:22:04 +1000 Subject: [PATCH 033/369] Add missing link to GraphQL Cookbooks Overview page. --- pages/apis/graphql/graphql_cookbook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/apis/graphql/graphql_cookbook.md b/pages/apis/graphql/graphql_cookbook.md index c601cea2c5..d154dd8710 100644 --- a/pages/apis/graphql/graphql_cookbook.md +++ b/pages/apis/graphql/graphql_cookbook.md @@ -15,6 +15,6 @@ There are recipes for a range of different topics, including: - [Packages](/docs/apis/graphql/cookbooks/packages) - [Pipelines](/docs/apis/graphql/cookbooks/pipelines) - [Pipeline templates](/docs/apis/graphql/cookbooks/pipeline-templates) -- [] +- [Rules](/docs/apis/graphql/cookbooks/rules) - [Organizations](/docs/apis/graphql/cookbooks/organizations) - [Teams](/docs/apis/graphql/cookbooks/teams) From 2c6282c2add992e6f193ffcfb8ec589f80ba8242 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 30 Aug 2024 17:25:16 +1000 Subject: [PATCH 034/369] Ran rake to rebuild the GraphQL nav. --- data/nav_graphql.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/nav_graphql.yml b/data/nav_graphql.yml index 512ea4fd01..d5a2fe2398 100644 --- a/data/nav_graphql.yml +++ b/data/nav_graphql.yml @@ -24,6 +24,8 @@ path: apis/graphql/cookbooks/pipelines - name: Pipeline templates path: apis/graphql/cookbooks/pipeline-templates + - name: Rules + path: apis/graphql/cookbooks/rules - name: Organizations path: apis/graphql/cookbooks/organizations - name: Teams From 5158fbe8cadf49d402928f70945dfd779c5265ac Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 2 Sep 2024 09:31:59 +1000 Subject: [PATCH 035/369] Add spec test for Rules entry. --- spec/scripts/graphql_api_content/nav_data_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/scripts/graphql_api_content/nav_data_spec.rb b/spec/scripts/graphql_api_content/nav_data_spec.rb index f4b7ac83d0..0db680dc79 100644 --- a/spec/scripts/graphql_api_content/nav_data_spec.rb +++ b/spec/scripts/graphql_api_content/nav_data_spec.rb @@ -341,6 +341,10 @@ "name" => "Pipeline templates", "path" => "apis/graphql/cookbooks/pipeline-templates" }, + { + "name" => "Rules", + "path" => "apis/graphql/cookbooks/rules" + }, { "name" => "Organizations", "path" => "apis/graphql/cookbooks/organizations" From 7718acfc3d8a86361e8fef38eac88b76a1672d90 Mon Sep 17 00:00:00 2001 From: Chris Campbell Date: Mon, 2 Sep 2024 10:03:55 +1000 Subject: [PATCH 036/369] Move static graphql navigation out of generation script --- config/initializers/nav.rb | 2 +- data/nav.yml | 35 ++++++++++++- data/nav_graphql.yml | 32 ------------ scripts/graphql_api_content/nav_data.rb | 65 ------------------------- 4 files changed, 35 insertions(+), 99 deletions(-) diff --git a/config/initializers/nav.rb b/config/initializers/nav.rb index 1aa301a0b9..0f28a0b164 100644 --- a/config/initializers/nav.rb +++ b/config/initializers/nav.rb @@ -5,7 +5,7 @@ .find { |item| item["name"] == "APIs" }["children"] .find { |item| item["name"] == "GraphQL" } - graphql_nav_item["children"] = YAML.load_file(File.join(Rails.root, 'data', 'nav_graphql.yml')) + graphql_nav_item["children"].concat(YAML.load_file(File.join(Rails.root, 'data', 'nav_graphql.yml'))) config.default_nav = Nav.new(nav_data) end diff --git a/data/nav.yml b/data/nav.yml index c83aae8eb8..6e59535dce 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -597,7 +597,40 @@ - name: "Suites" path: "apis/rest-api/teams/suites" - name: "GraphQL" - children: [] + children: + - name: Overview + path: apis/graphql-api + - name: Console and CLI tutorial + path: apis/graphql/graphql-tutorial + - name: Cookbook + children: + - name: Overview + path: apis/graphql/graphql-cookbook + - name: Agents + path: apis/graphql/cookbooks/agents + - name: Artifacts + path: apis/graphql/cookbooks/artifacts + - name: Builds + path: apis/graphql/cookbooks/builds + - name: Clusters + path: apis/graphql/cookbooks/clusters + - name: Jobs + path: apis/graphql/cookbooks/jobs + - name: Packages + path: apis/graphql/cookbooks/packages + - name: Pipelines + path: apis/graphql/cookbooks/pipelines + - name: Pipeline templates + path: apis/graphql/cookbooks/pipeline-templates + - name: Rules + path: apis/graphql/cookbooks/rules + - name: Organizations + path: apis/graphql/cookbooks/organizations + - name: Teams + path: apis/graphql/cookbooks/teams + - name: Limits + path: apis/graphql/graphql-resource-limits + - name: "Webhooks" children: - name: "Overview" diff --git a/data/nav_graphql.yml b/data/nav_graphql.yml index d5a2fe2398..0b00d8415e 100644 --- a/data/nav_graphql.yml +++ b/data/nav_graphql.yml @@ -1,37 +1,5 @@ # This file is generated by scripts/generate_graphql_api_content.rb. DO NOT EDIT. --- -- name: Overview - path: apis/graphql-api -- name: Console and CLI tutorial - path: apis/graphql/graphql-tutorial -- name: Cookbook - children: - - name: Overview - path: apis/graphql/graphql-cookbook - - name: Agents - path: apis/graphql/cookbooks/agents - - name: Artifacts - path: apis/graphql/cookbooks/artifacts - - name: Builds - path: apis/graphql/cookbooks/builds - - name: Clusters - path: apis/graphql/cookbooks/clusters - - name: Jobs - path: apis/graphql/cookbooks/jobs - - name: Packages - path: apis/graphql/cookbooks/packages - - name: Pipelines - path: apis/graphql/cookbooks/pipelines - - name: Pipeline templates - path: apis/graphql/cookbooks/pipeline-templates - - name: Rules - path: apis/graphql/cookbooks/rules - - name: Organizations - path: apis/graphql/cookbooks/organizations - - name: Teams - path: apis/graphql/cookbooks/teams -- name: Limits - path: apis/graphql/graphql-resource-limits - name: Queries children: - name: agent diff --git a/scripts/graphql_api_content/nav_data.rb b/scripts/graphql_api_content/nav_data.rb index 08dcb7ffdb..11517f92e0 100644 --- a/scripts/graphql_api_content/nav_data.rb +++ b/scripts/graphql_api_content/nav_data.rb @@ -16,71 +16,6 @@ def convert_to_nav_items(type_set, sub_dir = nil) def generate_graphql_nav_data(type_sets) [ - { - "name" => "Overview", - "path" => "apis/graphql-api" - }, - { - "name" => "Console and CLI tutorial", - "path" => "apis/graphql/graphql-tutorial" - }, - { - "name" => "Cookbook", - "children" => [ - { - "name" => "Overview", - "path" => "apis/graphql/graphql-cookbook" - }, - { - "name" => "Agents", - "path" => "apis/graphql/cookbooks/agents" - }, - { - "name" => "Artifacts", - "path" => "apis/graphql/cookbooks/artifacts" - }, - { - "name" => "Builds", - "path" => "apis/graphql/cookbooks/builds" - }, - { - "name" => "Clusters", - "path" => "apis/graphql/cookbooks/clusters" - }, - { - "name" => "Jobs", - "path" => "apis/graphql/cookbooks/jobs" - }, - { - "name" => "Packages", - "path" => "apis/graphql/cookbooks/packages" - }, - { - "name" => "Pipelines", - "path" => "apis/graphql/cookbooks/pipelines" - }, - { - "name" => "Pipeline templates", - "path" => "apis/graphql/cookbooks/pipeline-templates" - }, - { - "name" => "Rules", - "path" => "apis/graphql/cookbooks/rules" - }, - { - "name" => "Organizations", - "path" => "apis/graphql/cookbooks/organizations" - }, - { - "name" => "Teams", - "path" => "apis/graphql/cookbooks/teams" - } - ] - }, - { - "name" => "Limits", - "path" => "apis/graphql/graphql-resource-limits" - }, { "name" => "Queries", "children" => convert_to_nav_items(type_sets["query_types"], "query") From 159304bca294e2ab7d6205788d83003c72a6ac3d Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 2 Sep 2024 17:09:51 +1000 Subject: [PATCH 037/369] Complete t/w review of rules overview page. --- data/nav.yml | 2 +- .../rules/{manage_rules.md => manage.md} | 0 pages/pipelines/rules/overview.md | 37 +++++++++++-------- 3 files changed, 23 insertions(+), 16 deletions(-) rename pages/pipelines/rules/{manage_rules.md => manage.md} (100%) diff --git a/data/nav.yml b/data/nav.yml index c83aae8eb8..48ab79333c 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -331,7 +331,7 @@ - name: "Overview" path: "pipelines/rules/overview" - name: "Manage rules" - path: "pipelines/rules/manage-rules" + path: "pipelines/rules/manage" - name: "Security" children: - name: "Overview" diff --git a/pages/pipelines/rules/manage_rules.md b/pages/pipelines/rules/manage.md similarity index 100% rename from pages/pipelines/rules/manage_rules.md rename to pages/pipelines/rules/manage.md diff --git a/pages/pipelines/rules/overview.md b/pages/pipelines/rules/overview.md index 38a4813cdf..8a25240e3d 100644 --- a/pages/pipelines/rules/overview.md +++ b/pages/pipelines/rules/overview.md @@ -6,15 +6,18 @@ Rules express that an action (for example, triggering a build) is allowed betwee Rules are used to grant or restrict access between resources that would normally be determined by the default permissions. -## Available rule types +## Rule types ### pipeline.trigger_build.pipeline -Allows one pipeline to trigger another. This is useful where you want to allow a pipeline to trigger a build in another cluster, or if you want to allow a public pipeline to trigger a private one. +This rule type: -Note that this rule type overrides the usual [trigger step permissions checks](/docs/pipelines/trigger-step#permissions) on users and teams. +- Allows one pipeline to trigger another. +- Is useful where you want to allow a pipeline to trigger a build in another cluster, or if you want to allow a public pipeline to trigger a private one. -Rule document: +**Note:** This rule type overrides the usual [trigger step permissions checks](/docs/pipelines/trigger-step#permissions) on users and teams. + +Rule format: ```json { @@ -26,22 +29,24 @@ Rule document: } ``` -Value fields: +where: + +- `source_pipeline_uuid` is the UUID of the pipeline that's allowed to trigger another pipeline. +- `target_pipeline_uuid` is the UUID of the pipeline that can be triggered by the `source_pipeline_uuid` pipeline. -- `source_pipeline_uuid` The UUID of the pipeline that is allowed to trigger another pipeline. -- `target_pipeline_uuid` The UUID of the pipeline that is allowed to be triggered by the `source_pipeline_uuid` pipeline. +Learn more about how to create rules in [Manage rules](/docs/pipelines/rules/manage). #### Example use case: cross-cluster pipeline triggering -Clusters may be used to separate the environments necessary for building and deploying an application. For example, a CI pipeline in cluster A and a CD pipeline cluster B. Ordinarily, pipelines in separate clusters like this are not able to trigger builds for each other due to the strict isolation of clusters. +Clusters may be used to separate the environments necessary for building and deploying an application. For example, a continuous integration (CI) pipeline has been set up in cluster A and likewise, a continuous deployment (CD) pipeline in cluster B. Ordinarily, pipelines in separate clusters are not able to trigger builds between each other due to the strict isolation of clusters. -A `pipeline.trigger_build.pipeline` rule would allow a trigger step in the CI pipeline in cluster A to target the CD pipeline in cluster B. This would allow deploys to be triggered upon a successful CI build, while still maintaining the separation of the CI and CD agents in their respective clusters. +However, a `pipeline.trigger_build.pipeline` rule would allow a trigger step in the CI pipeline of cluster A to target the CD pipeline in cluster B. Such rules would allow deployment to be triggered upon a successful CI build, while still maintaining the separation between the CI and CD agents in their respective clusters. ### pipeline.artifacts_read.pipeline -Allows a source pipeline in one cluster to read artifacts from a target pipeline in another cluster. +This rule type allows a source pipeline in one cluster to read artifacts from a target pipeline in another cluster. -Rule document: +Rule format: ```json { @@ -53,11 +58,13 @@ Rule document: } ``` -Value fields: +where: -- `source_pipeline_uuid` The UUID of the pipeline that is allowed to read artifacts from another pipeline. -- `target_pipeline_uuid` The UUID of the pipeline that is allowed to have its artifacts read by jobs in the `source_pipeline_uuid` pipeline. +- `source_pipeline_uuid` is the UUID of the pipeline that's allowed to read artifacts from another pipeline. +- `target_pipeline_uuid` is the UUID of the pipeline whose artifacts can be read by jobs in the `source_pipeline_uuid` pipeline. #### Example use case: sharing assets between clusters -By default, artifacts cannot be accessed by pipelines in separate clusters. For example, a deploy pipeline in cluster B cannot ordinarily access artifacts uploaded by a CI pipeline in cluster A. A `pipeline.artifacts_read.pipeline` rule can be used to override this. For example, frontend assets uploaded as artifacts by the CI pipeline would now be accessible to the deploy pipeline via the `buildkite-agent artifact download --build xxx` command. +Artifacts are not accessible between pipelines across different clusters. For example, a deployment pipeline in cluster B cannot ordinarily access artifacts uploaded by a CI pipeline in cluster A. + +However, a `pipeline.artifacts_read.pipeline` rule can be used to override this restriction. For example, frontend assets uploaded as artifacts by the CI pipeline would now be accessible to the deployment pipeline via the `buildkite-agent artifact download --build xxx` command. From 05db4215bd8c14f149e71053bcab7250a1587e61 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Tue, 3 Sep 2024 12:03:11 +1000 Subject: [PATCH 038/369] Restructure rules landing page to remove '/overview' requirement from URL path, and tweak intro and 'create a rule' section of 'manage rules' page. --- data/nav.yml | 2 +- .../pipelines/{rules/overview.md => rules.md} | 18 ++++++++++-------- pages/pipelines/rules/manage.md | 16 ++++++++-------- 3 files changed, 19 insertions(+), 17 deletions(-) rename pages/pipelines/{rules/overview.md => rules.md} (86%) diff --git a/data/nav.yml b/data/nav.yml index 48ab79333c..45a4ba355d 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -329,7 +329,7 @@ - name: "Rules" children: - name: "Overview" - path: "pipelines/rules/overview" + path: "pipelines/rules" - name: "Manage rules" path: "pipelines/rules/manage" - name: "Security" diff --git a/pages/pipelines/rules/overview.md b/pages/pipelines/rules.md similarity index 86% rename from pages/pipelines/rules/overview.md rename to pages/pipelines/rules.md index 8a25240e3d..c2f673e06e 100644 --- a/pages/pipelines/rules/overview.md +++ b/pages/pipelines/rules.md @@ -17,14 +17,14 @@ This rule type: **Note:** This rule type overrides the usual [trigger step permissions checks](/docs/pipelines/trigger-step#permissions) on users and teams. -Rule format: +**Rule Document** format: ```json { "rule": "pipeline.trigger_build.pipeline", "value": { - "source_pipeline_uuid": "{triggering-pipeline-uuid}", - "target_pipeline_uuid": "{triggered-pipeline-uuid}" + "source_pipeline_uuid": "pipeline-uuid", + "target_pipeline_uuid": "pipeline-uuid" } } ``` @@ -34,7 +34,7 @@ where: - `source_pipeline_uuid` is the UUID of the pipeline that's allowed to trigger another pipeline. - `target_pipeline_uuid` is the UUID of the pipeline that can be triggered by the `source_pipeline_uuid` pipeline. -Learn more about how to create rules in [Manage rules](/docs/pipelines/rules/manage). +Learn more about creating rules in [Manage rules](/docs/pipelines/rules/manage). #### Example use case: cross-cluster pipeline triggering @@ -46,14 +46,14 @@ However, a `pipeline.trigger_build.pipeline` rule would allow a trigger step in This rule type allows a source pipeline in one cluster to read artifacts from a target pipeline in another cluster. -Rule format: +**Rule Document** format: ```json { - "rule": "pipeline.trigger_build.pipeline", + "rule": "pipeline.artifacts_read.pipeline", "value": { - "source_pipeline_uuid": "{uuid-of-source-pipeline}", - "target_pipeline_uuid": "{uuid-of-target-pipeline}" + "source_pipeline_uuid": "pipeline-uuid", + "target_pipeline_uuid": "pipeline-uuid" } } ``` @@ -63,6 +63,8 @@ where: - `source_pipeline_uuid` is the UUID of the pipeline that's allowed to read artifacts from another pipeline. - `target_pipeline_uuid` is the UUID of the pipeline whose artifacts can be read by jobs in the `source_pipeline_uuid` pipeline. +Learn more about creating rules in [Manage rules](/docs/pipelines/rules/manage). + #### Example use case: sharing assets between clusters Artifacts are not accessible between pipelines across different clusters. For example, a deployment pipeline in cluster B cannot ordinarily access artifacts uploaded by a CI pipeline in cluster A. diff --git a/pages/pipelines/rules/manage.md b/pages/pipelines/rules/manage.md index ffc63e9909..3fec61a6b6 100644 --- a/pages/pipelines/rules/manage.md +++ b/pages/pipelines/rules/manage.md @@ -1,22 +1,22 @@ # Manage rules +This page provides details on how to manage [rules](/docs/pipelines/rules) within your Buildkite organization. + ## Create a rule -Organization admins can create new rules using the **Rules** page in **Organization settings**, as well as via the Buildkite [REST API](/docs/apis/rest-api) and [GraphQL API](/docs/apis/graphql-api). +New rules can be created by [Buildkite organization administrators](/docs/team-management/permissions#manage-teams-and-permissions-organization-level-permissions) using the [**Rules** page](#create-a-rule-using-the-buildkite-interface), as well as the [REST API's](#create-a-rule-using-the-rest-api) or [GraphQL API's](#create-a-rule-using-the-graphql-api) create a rule feature. ### Using the Buildkite interface -To create a new rule using the Buildkite UI: - -1. Select **Settings** in the global navigation to access the **Organization settings** page. +To create a new rule using the Buildkite interface: -1. Select **Rules** in the Pipelines section. +1. Select **Settings** in the global navigation to access the [**Organization Settings**](https://buildkite.com/organizations/~/settings) page. -1. Select **New Rule**. +1. In the **Pipelines** section, select **Rules** > **New Rule** to open its page. -1. Under **Rule Type**, select the type of rule you want to create, such as `pipeline.trigger_build.pipeline`. +1. Under **Rule Template**, select the [type of rule](/docs/pipelines/rules#rule-types) to be created, that is, either **pipeline.trigger_build.pipeline** or **pipeline.artifacts_read.pipeline**. -1. Under **Rule Document**, populate the relevant data. For example, if you're creating a `pipeline.trigger_build.pipeline` rule, you'll need to provide a `source_pipeline_uuid` and a `target_pipeline_uuid`. You can find the UUIDs of your pipelines on their **Settings** page under the **GraphQL API integration** section. +1. Under **Rule Document**, specify the relevant `pipeline-uuid` (UUID) values for both the `source_pipeline_uuid` and `target_pipeline_uuid` pipelines, of your [**pipeline.trigger_build.pipeline**](/docs/pipelines/rules#rule-types-pipeline-dot-trigger-build-dot-pipeline) or or [**pipeline.artifacts_read.pipeline**](/docs/pipelines/rules#rule-types-pipeline-dot-artifacts-read-dot-pipeline) rule. You can find the UUID values for these pipelines on the pipelines' respective **Settings** page under the **GraphQL API integration** section. 1. Select **Submit**. From 36de2d43ba702fd5b7e8da3b0f746f622f941a55 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Tue, 3 Sep 2024 13:46:05 +1000 Subject: [PATCH 039/369] Fix broken links. --- pages/apis/rest_api/rules.md | 4 ++-- pages/clusters/overview.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pages/apis/rest_api/rules.md b/pages/apis/rest_api/rules.md index 9aecd618d1..ba9fe8c07d 100644 --- a/pages/apis/rest_api/rules.md +++ b/pages/apis/rest_api/rules.md @@ -4,7 +4,7 @@ The rules API lets you create and manage rules in your organization. ## Rules -[Rules](/docs/pipelines/rules/overview) allow you to manage permissions between Buildkite resources. +[Rules](/docs/pipelines/rules) allow you to manage permissions between Buildkite resources. A rule is used to specify that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). Rules allow you to break out of the defaults provided by Buildkite such as the isolation between [clusters](/docs/clusters/overview). @@ -130,7 +130,7 @@ Required [request body properties](/docs/api#request-body-properties): - diff --git a/pages/clusters/overview.md b/pages/clusters/overview.md index 804f04817f..24e8f89eaf 100644 --- a/pages/clusters/overview.md +++ b/pages/clusters/overview.md @@ -33,7 +33,7 @@ You can create as many clusters as your require for your setup. Learn more about working with clusters in [Manage clusters](/docs/clusters/manage-clusters). > 📘 Pipeline triggering -> Pipelines associated with one cluster cannot trigger pipelines associated with another cluster, unless a [rule](/docs/pipelines/rules/overview) has been created to explicitly allow triggering between pipelines in different clusters. +> Pipelines associated with one cluster cannot trigger pipelines associated with another cluster, unless a [rule](/docs/pipelines/rules) has been created to explicitly allow triggering between pipelines in different clusters. ### How should I structure my queues From a35cb847f882bb89e19037b20802d70dffa8261c Mon Sep 17 00:00:00 2001 From: Jarryd Tilbrook Date: Tue, 3 Sep 2024 13:03:51 +0800 Subject: [PATCH 040/369] Add new top level commands to output --- pages/cli.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pages/cli.md b/pages/cli.md index dad2e34543..a1d58f019e 100644 --- a/pages/cli.md +++ b/pages/cli.md @@ -24,6 +24,7 @@ $ bk build view Available Commands: agent Manage agents + api Interact with the Buildkite API build Manage pipeline builds cluster Manage organization clusters completion Generate the autocompletion script for the specified shell @@ -31,6 +32,7 @@ Available Commands: help Help about any command init Initialize a pipeline.yaml file job Manage jobs within a build + package Manage packages pipeline Manage pipelines use Select an organization From ef494cec23608f8d83a6750687229203449031a3 Mon Sep 17 00:00:00 2001 From: James Healy Date: Tue, 3 Sep 2024 16:37:21 +1000 Subject: [PATCH 041/369] A few tweaks to our report-only CSP header Our CSP is in report-only mode, but we'd like to get it closer to being enforcable. As a step in that direction I've opened a few pages in production, audited the most common CSP warnings in the browser console, and this should resolve them. These are all expected tools, our policy has just bitrotted, or the vendor has changed their resources. 1. object_src: we can't include `none` alongside an actual value 2. connect_src: we load GA v4 from www.googletagmanager.com, but it wants to submit data to https://www.google-analytics.com 3. connect_src: helpscout beacon wants tosend data to a cloudfront distribution CSP docs for Helpscout beacon (mentions the cloudfront domain): https://docs.helpscout.com/article/815-csp-settings-for-beacon CSP docs for datadog real user monitoring: https://docs.datadoghq.com/integrations/content_security_policy_logs/ I also added some comments as context for future travelers. --- config/initializers/content_security_policy.rb | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 94f46d169a..92ca6f6498 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -19,7 +19,7 @@ Rails.application.config.content_security_policy do |policy| policy.default_src :self policy.font_src :self, "https://www2.buildkiteassets.com/" - policy.object_src :none, "https://beacon-v2.helpscout.net" + policy.object_src "https://beacon-v2.helpscout.net" policy.style_src :self, :unsafe_inline, "https://beacon-v2.helpscout.net" policy.img_src( @@ -44,6 +44,8 @@ policy.script_src *policy.script_src, :unsafe_eval, "http://#{ ViteRuby.config.host_with_port }" if Rails.env.development? policy.connect_src( + "https://www.google-analytics.com", + # allow AJAX queries against our search vendor "https://#{ENV['ALGOLIA_APP_ID']}-dsn.algolia.net", "https://#{ENV['ALGOLIA_APP_ID']}-1.algolianet.com", @@ -52,14 +54,22 @@ "https://cdn.segment.com/", "https://api.segment.io/", + + # We have Datadog Real User Monitoring enabled + "https://rum.browser-intake-datadoghq.com", + + # For collecting feedback from customers "https://emojicom.io/", + + # helpscout beacon sends data to two places "https://beacon-v2.helpscout.net", - "https://rum.browser-intake-datadoghq.com" + "https://d3hb14vkzrxvla.cloudfront.net", ) # Allow @vite/client to hot reload changes in development policy.connect_src *policy.connect_src, "ws://#{ ViteRuby.config.host_with_port }" if Rails.env.development? + # For collecting feedback from customers policy.frame_src( "https://cdn.emojicom.io/" ) From d07d3786d372b05c33ed16ab45dc8c6fc51f49d1 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Tue, 3 Sep 2024 17:05:49 +1000 Subject: [PATCH 042/369] Fix up Manage rules page. And a few more tweaks to the rules landing page. --- pages/apis/descriptions/_graphql_rule_id.md | 28 ---- pages/apis/descriptions/_rest_access_token.md | 2 +- .../apis/descriptions/_rest_pipeline_uuid.md | 24 --- pages/apis/descriptions/_rest_rule_uuid.md | 10 -- pages/clusters/manage_clusters.md | 4 +- pages/pipelines/rules.md | 22 ++- pages/pipelines/rules/manage.md | 137 +++++++++++++++--- 7 files changed, 137 insertions(+), 90 deletions(-) delete mode 100644 pages/apis/descriptions/_graphql_rule_id.md delete mode 100644 pages/apis/descriptions/_rest_pipeline_uuid.md delete mode 100644 pages/apis/descriptions/_rest_rule_uuid.md diff --git a/pages/apis/descriptions/_graphql_rule_id.md b/pages/apis/descriptions/_graphql_rule_id.md deleted file mode 100644 index ea8538d60b..0000000000 --- a/pages/apis/descriptions/_graphql_rule_id.md +++ /dev/null @@ -1,28 +0,0 @@ -- `ruleId` (required) can be obtained: - - * From the **Rules** section of your **Organization Settings** page, accessed by selecting **Settings** in the global navigation of your organization in Buildkite. - - * By running a [List rules](/docs/apis/graphql/cookbooks/rules#list-rules) GraphQL API query and obtaining this value from the `id` in the response associated with the rule type, source and target of the rule you wish to find (specified by the `type`, `source` and `target` values in the response). For example: - - ```graphql - query getRules { - organization(slug: "organization-slug") { - rules(first: 10) { - edges { - node { - id - type - source { - ... on Pipeline - slug - } - target { - ... on Pipeline - slug - } - } - } - } - } - } - ``` diff --git a/pages/apis/descriptions/_rest_access_token.md b/pages/apis/descriptions/_rest_access_token.md index e0ef385349..8eb70664bf 100644 --- a/pages/apis/descriptions/_rest_access_token.md +++ b/pages/apis/descriptions/_rest_access_token.md @@ -1 +1 @@ -- `$TOKEN` is an [API access token](https://buildkite.com/user/api-access-tokens) scoped to the relevant **Organization** and **REST API Scopes** that your agent needs access to in Buildkite. +- `$TOKEN` is an [API access token](https://buildkite.com/user/api-access-tokens) scoped to the relevant **Organization** and **REST API Scopes** that your request needs access to in Buildkite. diff --git a/pages/apis/descriptions/_rest_pipeline_uuid.md b/pages/apis/descriptions/_rest_pipeline_uuid.md deleted file mode 100644 index 36e13ff2a8..0000000000 --- a/pages/apis/descriptions/_rest_pipeline_uuid.md +++ /dev/null @@ -1,24 +0,0 @@ -- `{pipeline.uuid}` can be obtained: - - * From the **Pipeline Settings** page of a given pipeline. To do this: - 1. Select **Pipelines** (in the global navigation) > the specific pipeline > **Settings**. - 1. Once on the **Pipeline Settings** page, copy the `UUID` value from the **GraphQL API Integration** section, which is the `{pipeline.uuid}` value. - - * By running a [Get pipeline](/docs/apis/rest-api/pipelines#get-a-pipeline) REST API query and obtaining this value from the `id` in the response. For example: - - ```bash - curl -H "Authorization: Bearer $TOKEN" \ - - X GET "https://api.buildkite.com/v2/organizations/{org.slug}/pipelines/{pipeline.slug}" - ``` - - * By running a [Get pipeline](/docs/apis/graphql/schemas/query/pipeline) GraphQL API query and obtaining this value from the `uuid` in the response. For example: - - ```graphql - query getPipeline { - organization(slug: "organization-slug") { - pipeline(slug: "pipeline-slug") { - uuid - } - } - } - ``` diff --git a/pages/apis/descriptions/_rest_rule_uuid.md b/pages/apis/descriptions/_rest_rule_uuid.md deleted file mode 100644 index 34d5fcde60..0000000000 --- a/pages/apis/descriptions/_rest_rule_uuid.md +++ /dev/null @@ -1,10 +0,0 @@ -- `{rule.uuid}` can be obtained: - - * From the **Rules** section of your **Organization Settings** page, accessed by selecting **Settings** in the global navigation of your organization in Buildkite. - - * By running a [List rules](/docs/apis/rest-api/rules#rules-list-rules) REST API query and obtaining this value from the `uuid` in the response associated with the rule type, source and target of the rule you wish to find (specified by the `type`, `source` and `target` values in the response). For example: - - ```bash - curl -H "Authorization: Bearer $TOKEN" \ - - X GET "https://api.buildkite.com/v2/organizations/{org.slug}/rules" - ``` diff --git a/pages/clusters/manage_clusters.md b/pages/clusters/manage_clusters.md index b49536b629..c9f7b5bcf4 100644 --- a/pages/clusters/manage_clusters.md +++ b/pages/clusters/manage_clusters.md @@ -352,7 +352,7 @@ where: 1. Select **Pipelines** in the global navigation > the specific pipeline to be moved to the cluster > **Settings**. 1. Copy the **ID** shown in the **GraphQL API Integration** section of this page, which is this `id` value. - * By running the `getCurrentUsersOrgs` GraphQL API query to obtain the organization slugs for the current user's accessible organizations, [getOrgPipelines](/docs/apis/graphql/schemas/query/organization) query to obtain the pipeline's `id` in the response. For example: + * By running the `getCurrentUsersOrgs` GraphQL API query to obtain the organization slugs for the current user's accessible organizations, then [getOrgPipelines](/docs/apis/graphql/schemas/query/organization) query to obtain the pipeline's `id` in the response. For example: Step 1. Run `getCurrentUsersOrgs` to obtain the organization slug values in the response for the current user's accessible organizations: @@ -383,7 +383,7 @@ where: uuid name } - } + } } } } diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index c2f673e06e..a64c4f020a 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -8,12 +8,17 @@ Rules are used to grant or restrict access between resources that would normally ## Rule types +Buildkite Pipelines supports two types of rules that allow one pipeline build to: + +- [Trigger another pipeline build](#rule-types-pipeline-dot-trigger-build-dot-pipeline). +- [Read the artifacts generated by another pipeline build](#rule-types-pipeline-dot-artifacts-read-dot-pipeline). + ### pipeline.trigger_build.pipeline -This rule type: +This rule type allows one pipeline to trigger another, where: -- Allows one pipeline to trigger another. -- Is useful where you want to allow a pipeline to trigger a build in another cluster, or if you want to allow a public pipeline to trigger a private one. +- Both pipelines are in the same or different [clusters](/docs/clusters/overview). +- One pipeline is public and another is private. **Note:** This rule type overrides the usual [trigger step permissions checks](/docs/pipelines/trigger-step#permissions) on users and teams. @@ -44,7 +49,10 @@ However, a `pipeline.trigger_build.pipeline` rule would allow a trigger step in ### pipeline.artifacts_read.pipeline -This rule type allows a source pipeline in one cluster to read artifacts from a target pipeline in another cluster. +This rule type allows one pipeline to access (that is, with read-only permissions) the artifacts built by another, where: + +- Both pipelines are in the same or different [clusters](/docs/clusters/overview). +- One pipeline is public and another is private. **Rule Document** format: @@ -60,8 +68,8 @@ This rule type allows a source pipeline in one cluster to read artifacts from a where: -- `source_pipeline_uuid` is the UUID of the pipeline that's allowed to read artifacts from another pipeline. -- `target_pipeline_uuid` is the UUID of the pipeline whose artifacts can be read by jobs in the `source_pipeline_uuid` pipeline. +- `source_pipeline_uuid` is the UUID of the pipeline that's allowed to access the artifacts from another pipeline. +- `target_pipeline_uuid` is the UUID of the pipeline whose artifacts can be accessed by jobs in the `source_pipeline_uuid` pipeline. Learn more about creating rules in [Manage rules](/docs/pipelines/rules/manage). @@ -69,4 +77,4 @@ Learn more about creating rules in [Manage rules](/docs/pipelines/rules/manage). Artifacts are not accessible between pipelines across different clusters. For example, a deployment pipeline in cluster B cannot ordinarily access artifacts uploaded by a CI pipeline in cluster A. -However, a `pipeline.artifacts_read.pipeline` rule can be used to override this restriction. For example, frontend assets uploaded as artifacts by the CI pipeline would now be accessible to the deployment pipeline via the `buildkite-agent artifact download --build xxx` command. +However, a `pipeline.artifacts_read.pipeline` rule can be used to override this restriction. For example, assets uploaded as artifacts by the CI pipeline would now be accessible to the deployment pipeline via the `buildkite-agent artifact download --build xxx` command. diff --git a/pages/pipelines/rules/manage.md b/pages/pipelines/rules/manage.md index 3fec61a6b6..b5643e4a95 100644 --- a/pages/pipelines/rules/manage.md +++ b/pages/pipelines/rules/manage.md @@ -14,12 +14,14 @@ To create a new rule using the Buildkite interface: 1. In the **Pipelines** section, select **Rules** > **New Rule** to open its page. -1. Under **Rule Template**, select the [type of rule](/docs/pipelines/rules#rule-types) to be created, that is, either **pipeline.trigger_build.pipeline** or **pipeline.artifacts_read.pipeline**. +1. Under **Rule Type**, select the [type of rule](/docs/pipelines/rules#rule-types) to be created, that is, either **pipeline.trigger_build.pipeline** or **pipeline.artifacts_read.pipeline**. -1. Under **Rule Document**, specify the relevant `pipeline-uuid` (UUID) values for both the `source_pipeline_uuid` and `target_pipeline_uuid` pipelines, of your [**pipeline.trigger_build.pipeline**](/docs/pipelines/rules#rule-types-pipeline-dot-trigger-build-dot-pipeline) or or [**pipeline.artifacts_read.pipeline**](/docs/pipelines/rules#rule-types-pipeline-dot-artifacts-read-dot-pipeline) rule. You can find the UUID values for these pipelines on the pipelines' respective **Settings** page under the **GraphQL API integration** section. +1. Under **Rule Document**, specify the relevant `pipeline-uuid` (UUID) values for both the `source_pipeline_uuid` and `target_pipeline_uuid` pipelines, of your [**pipeline.trigger_build.pipeline**](/docs/pipelines/rules#rule-types-pipeline-dot-trigger-build-dot-pipeline) or [**pipeline.artifacts_read.pipeline**](/docs/pipelines/rules#rule-types-pipeline-dot-artifacts-read-dot-pipeline) rule. You can find the UUID values for these pipelines on the pipelines' respective **Settings** page under the **GraphQL API integration** section. 1. Select **Submit**. + The rule is created and presented on the **Rules** page, with a description of the rule type and the relationship between both pipelines. + ### Using the REST API To [create a new rule](/docs/apis/rest-api/rules#rules-create-a-rule) using the [REST API](/docs/apis/rest-api), run the following example `curl` command: @@ -31,8 +33,8 @@ curl -H "Authorization: Bearer $TOKEN" \ -d '{ "rule": "pipeline.trigger_build.pipeline", "value": { - "source_pipeline_uuid": "{uuid-of-triggering-pipeline}", - "target_pipeline_uuid": "{uuid-of-target-pipeline}" + "source_pipeline_uuid": "{pipeline.uuid}", + "target_pipeline_uuid": "{pipeline.uuid}" } }' ``` @@ -43,18 +45,31 @@ where: <%= render_markdown partial: 'apis/descriptions/rest_org_slug' %> -<%= render_markdown partial: 'apis/descriptions/rest_pipeline_uuid' %> +- `rule` is the [type of rule](/docs/pipelines/rules#rule-types) to be created, that is, either `pipeline.trigger_build.pipeline` or `pipeline.artifacts_read.pipeline`. + +- `{pipeline.uuid}` value for `source_pipeline_uuid` and `target_pipeline_uuid` can be obtained: + + * From the **Pipeline Settings** page of the appropriate pipeline. To do this: + 1. Select **Pipelines** (in the global navigation) > the specific pipeline > **Settings**. + 1. Once on the **Pipeline Settings** page, copy the `UUID` value from the **GraphQL API Integration** section, which is the `{pipeline.uuid}` value. + + * By running the [List pipelines](/docs/apis/rest-api/pipelines#list-pipelines) REST API query to obtain this value from `id` in the response from the specific pipeline. For example: + + ```bash + curl -H "Authorization: Bearer $TOKEN" \ + - X GET "https://api.buildkite.com/v2/organizations/{org.slug}/pipelines" + ``` ### Using the GraphQL API -To [create a new rule](/docs/apis/graphql/schemas/mutation/rulecreate) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: +To [create a new rule](/docs/apis/graphql/cookbooks/rules#create-a-rule) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: ```graphql mutation { ruleCreate(input: { organizationId: "organization-id", type: "pipeline.trigger_build.pipeline", - value: "{\"source_pipeline_uuid\":\"{uuid-of-source-pipeline}\",\"target_pipeline_uuid\":\"{uuid-of-target-pipeline}\"}" + value: "{\"source_pipeline_uuid\":\"pipeline-uuid\",\"target_pipeline_uuid\":\"pipeline-uuid\"}" }) { rule { id @@ -86,20 +101,66 @@ where: <%= render_markdown partial: 'apis/descriptions/graphql_organization_id' %> -<%= render_markdown partial: 'apis/descriptions/rest_pipeline_uuid' %> +- `type` is the [type of rule](/docs/pipelines/rules#rule-types) to be created, that is, either `pipeline.trigger_build.pipeline` or `pipeline.artifacts_read.pipeline`. + +- `pipeline-uuid` value for `source_pipeline_uuid` and `target_pipeline_uuid` can be obtained: + + * From the **Pipeline Settings** page of the appropriate pipeline. To do this: + 1. Select **Pipelines** (in the global navigation) > the specific pipeline > **Settings**. + 1. Once on the **Pipeline Settings** page, copy the `UUID` value from the **GraphQL API Integration** section, which is the `pipeline-uuid` value. + + * By running the `getCurrentUsersOrgs` GraphQL API query to obtain the organization slugs for the current user's accessible organizations, then [getOrgPipelines](/docs/apis/graphql/schemas/query/organization) query to obtain the pipeline's `uuid` in the response. For example: + + Step 1. Run `getCurrentUsersOrgs` to obtain the organization slug values in the response for the current user's accessible organizations: + + ```graphql + query getCurrentUsersOrgs { + viewer { + organization { + edges { + node { + name + slug + } + } + } + } + } + ``` + + Step 2. Run `getOrgPipelines` with the appropriate slug value above to obtain this organization's `uuid` in the response: + + ```graphql + query getOrgPipelines { + organization(slug: "organization-slug") { + pipelines(first: 100) { + edges { + node { + id + uuid + name + } + } + } + } + } + ``` ## Delete a rule -Organization admins can delete rules using the **Rules** page in **Organization settings**, as well as via the Buildkite [REST API](/docs/apis/rest-api) and [GraphQL API](/docs/apis/graphql-api). +Rules can be deleted by [Buildkite organization administrators](/docs/team-management/permissions#manage-teams-and-permissions-organization-level-permissions) using the [**Rules** page](#delete-a-rule-using-the-buildkite-interface), as well as the [REST API's](#delete-a-rule-using-the-rest-api) or [GraphQL API's](#delete-a-rule-using-the-graphql-api) delete a rule feature. ### Using the Buildkite interface -To delete a rule using the Buildkite UI: +To delete a rule using the Buildkite interface: + +1. Select **Settings** in the global navigation to access the [**Organization Settings**](https://buildkite.com/organizations/~/settings) page. + +1. In the **Pipelines** section, select **Rules** to access its page. + +1. Expand the existing rule to be deleted. -1. Select **Settings** in the global navigation to access the **Organization settings** page. -2. Select **Rules** in the Pipelines section. -3. Select the rule you wish to delete. -4. Select **Delete** +1. Select the **Delete** button to delete this rule. ### Using the REST API @@ -107,7 +168,7 @@ To [delete a rule](/docs/apis/rest-api/rules#rules-delete-a-rule) using the [RES ```bash curl -H "Authorization: Bearer $TOKEN" \ - -X DELETE "https://api.buildkite.com/v2/organizations/{org.slug}/rules/{uuid}" + -X DELETE "https://api.buildkite.com/v2/organizations/{org.slug}/rules/{rule.uuid}" ``` where: @@ -116,11 +177,22 @@ where: <%= render_markdown partial: 'apis/descriptions/rest_org_slug' %> -<%= render_markdown partial: 'apis/descriptions/rest_rule_uuid' %> +- `{rule.uuid}` can be obtained: + + * From the **Rules** section of your **Organization Settings** page, accessed by selecting **Settings** in the global navigation of your organization in Buildkite. + + * By running a [List rules](/docs/apis/rest-api/rules#rules-list-rules) REST API query to obtain the rule's `uuid` in the response. For example: + + ```bash + curl -H "Authorization: Bearer $TOKEN" \ + - X GET "https://api.buildkite.com/v2/organizations/{org.slug}/rules" + ``` + + **Important:** For the rule identified by its `uuid` in the response, ensure the pipeline UUIDs of the source (`source_uuid`) and target (`target_uuid`), as well as the rule type (`type`) match those of this rule to be deleted. ### Using the GraphQL API -To [delete a rule](/docs/apis/graphql/schemas/mutation/ruledelete) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: +To [delete a rule](/docs/apis/graphql/cookbooks/rules#delete-a-rule) using the [GraphQL API](/docs/apis/graphql-api), run the following example mutation: ```graphql mutation { @@ -137,4 +209,33 @@ where: <%= render_markdown partial: 'apis/descriptions/graphql_organization_id' %> -<%= render_markdown partial: 'apis/descriptions/graphql_rule_id' %> +- `id` is the rule ID value, which can be obtained: + + * From the **Rules** section of your **Organization Settings** page, accessed by selecting **Settings** in the global navigation of your organization in Buildkite. + + * By running a [List rules](/docs/apis/graphql/cookbooks/rules#list-rules) GraphQL API query to obtain the rule's `id` in the response. For example: + + ```graphql + query getRules { + organization(slug: "organization-slug") { + rules(first: 10) { + edges { + node { + id + type + source { + ... on Pipeline + slug + } + target { + ... on Pipeline + slug + } + } + } + } + } + } + ``` + + **Important:** For the rule identified by its `uuid` in the response, ensure the pipeline UUIDs of the source (`source_uuid`) and target (`target_uuid`), as well as the rule type (`type`) match those of this rule to be deleted. From f3417496643cf8b43158e0b6984ac7e6e2fd62cf Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Tue, 3 Sep 2024 17:08:41 +1000 Subject: [PATCH 043/369] Fix pedantic linter. --- pages/pipelines/rules/manage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/pipelines/rules/manage.md b/pages/pipelines/rules/manage.md index b5643e4a95..6eb0f4d317 100644 --- a/pages/pipelines/rules/manage.md +++ b/pages/pipelines/rules/manage.md @@ -188,7 +188,7 @@ where: - X GET "https://api.buildkite.com/v2/organizations/{org.slug}/rules" ``` - **Important:** For the rule identified by its `uuid` in the response, ensure the pipeline UUIDs of the source (`source_uuid`) and target (`target_uuid`), as well as the rule type (`type`) match those of this rule to be deleted. + **Important:** For the rule identified by its `uuid` in the response, ensure the pipeline UUIDs of the source (`source_uuid`) and target (`target_uuid`), as well as the rule type (`type`) match those of this rule to be deleted. ### Using the GraphQL API @@ -238,4 +238,4 @@ where: } ``` - **Important:** For the rule identified by its `uuid` in the response, ensure the pipeline UUIDs of the source (`source_uuid`) and target (`target_uuid`), as well as the rule type (`type`) match those of this rule to be deleted. + **Important:** For the rule identified by its `uuid` in the response, ensure the pipeline UUIDs of the source (`source_uuid`) and target (`target_uuid`), as well as the rule type (`type`) match those of this rule to be deleted. From 459f4c9f3cd84bb74227f806fa19775106660457 Mon Sep 17 00:00:00 2001 From: Ben McNicholl Date: Wed, 4 Sep 2024 11:57:10 +1000 Subject: [PATCH 044/369] Add info on build time discrepency vs waterfall jobs --- pages/pipelines/waterfall.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pages/pipelines/waterfall.md b/pages/pipelines/waterfall.md index 1740155418..ce1ce33c59 100644 --- a/pages/pipelines/waterfall.md +++ b/pages/pipelines/waterfall.md @@ -30,3 +30,7 @@ You can hover over a bar to view these durations. Time is rounded to the nearest Group, matrix and parallel steps are shown with nested rows underneath a 'parent' row. A parent row displays a solid bar representing the total duration of its child rows. The bar is green if all child rows passed, and red if any of them failed. <%= image "waterfall-view-parent-row.png", alt: "Image showing an example of a parent row and its children in a waterfall chart" %> + +> 📘 Build time discrepency vs waterfall view +> Although canceled jobs will show as a blank line in the waterfall view, they still contribute to the total build time. If a job ran for 20 minutes and was then canceled, that job will appear as a blank line in the waterfall view, but contribute 20 minutes to the total build time. + From bcd919332529e79988d9f4c7462b4c4243f1fe68 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Wed, 4 Sep 2024 14:48:47 +1000 Subject: [PATCH 045/369] Final tweaks to rules' feature's API pages. Fix GraphQL query syntax error in listing orgs. --- .../apis/descriptions/_graphql_organization_id.md | 2 +- pages/apis/graphql/cookbooks/rules.md | 14 ++++++++------ pages/apis/rest_api/rules.md | 12 ++++++++---- pages/pipelines/_rules_summary.md | 5 +++++ pages/pipelines/rules.md | 6 ++---- pages/pipelines/rules/manage.md | 6 ++++-- 6 files changed, 28 insertions(+), 17 deletions(-) create mode 100644 pages/pipelines/_rules_summary.md diff --git a/pages/apis/descriptions/_graphql_organization_id.md b/pages/apis/descriptions/_graphql_organization_id.md index 3ee90a67cf..ab69ea84a5 100644 --- a/pages/apis/descriptions/_graphql_organization_id.md +++ b/pages/apis/descriptions/_graphql_organization_id.md @@ -9,7 +9,7 @@ ```graphql query getCurrentUsersOrgs { viewer { - organization { + organizations { edges { node { name diff --git a/pages/apis/graphql/cookbooks/rules.md b/pages/apis/graphql/cookbooks/rules.md index 7cb7e328f1..c809b1bf65 100644 --- a/pages/apis/graphql/cookbooks/rules.md +++ b/pages/apis/graphql/cookbooks/rules.md @@ -19,12 +19,14 @@ Get the first 10 rules and their information for an organization. targetType sourceType source { - ... on Pipeline + ... on Pipeline { slug + } } target { - ... on Pipeline + ... on Pipeline { slug + } } effect action @@ -41,7 +43,7 @@ Get the first 10 rules and their information for an organization. ## Create a rule -Create a rule. `value` must be a JSON encoded string. +Create a rule. The value of the `value` field must be a JSON-encoded string. ```graphql mutation { @@ -56,10 +58,10 @@ mutation { targetType sourceType source { - ... on Pipeline { - uuid - } + ... on Pipeline { + uuid } + } target { ... on Pipeline { uuid diff --git a/pages/apis/rest_api/rules.md b/pages/apis/rest_api/rules.md index ba9fe8c07d..4efe4f9a77 100644 --- a/pages/apis/rest_api/rules.md +++ b/pages/apis/rest_api/rules.md @@ -4,9 +4,9 @@ The rules API lets you create and manage rules in your organization. ## Rules -[Rules](/docs/pipelines/rules) allow you to manage permissions between Buildkite resources. +[Rules](/docs/pipelines/rules) do the following: -A rule is used to specify that an action is allowed between a source resource (e.g. a pipeline) and a target resource (e.g. another pipeline). Rules allow you to break out of the defaults provided by Buildkite such as the isolation between [clusters](/docs/clusters/overview). +<%= render_markdown partial: 'pipelines/rules_summary' %> ### List rules @@ -130,8 +130,12 @@ Required [request body properties](/docs/api#request-body-properties): - + diff --git a/pages/pipelines/_rules_summary.md b/pages/pipelines/_rules_summary.md new file mode 100644 index 0000000000..ceab3e2d1a --- /dev/null +++ b/pages/pipelines/_rules_summary.md @@ -0,0 +1,5 @@ +- Allow you to manage access to interactions between Buildkite resources. + +- Allow an action (for example, triggering a build) between a source resource (for example, a pipeline) and a target resource (for example, another pipeline) across your Buildkite organization. + +- Are used to grant access between resources that would normally be restricted by [cluster](/docs/clusters/overview), [visibility](/docs/pipelines/public-pipelines), or [permissions](/docs/team-management/permissions). diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index a64c4f020a..33d50bcc72 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -1,10 +1,8 @@ # Rules overview -Rules allow you to customize permissions between Buildkite resources. +Rules do the following: -Rules express that an action (for example, triggering a build) is allowed between a source resource (for example, a pipeline) and a target resource (for example, another pipeline). - -Rules are used to grant or restrict access between resources that would normally be determined by the default permissions. +<%= render_markdown partial: 'pipelines/rules_summary' %> ## Rule types diff --git a/pages/pipelines/rules/manage.md b/pages/pipelines/rules/manage.md index 6eb0f4d317..6a90c4edc8 100644 --- a/pages/pipelines/rules/manage.md +++ b/pages/pipelines/rules/manage.md @@ -224,12 +224,14 @@ where: id type source { - ... on Pipeline + ... on Pipeline { slug + } } target { - ... on Pipeline + ... on Pipeline { slug + } } } } From 0c10f0edb184bff6c8e296f002fe048f8dcb2125 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Wed, 4 Sep 2024 15:02:19 +1000 Subject: [PATCH 046/369] Streamline rules intro a little more. --- pages/pipelines/_rules_summary.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pages/pipelines/_rules_summary.md b/pages/pipelines/_rules_summary.md index ceab3e2d1a..dda32b90b6 100644 --- a/pages/pipelines/_rules_summary.md +++ b/pages/pipelines/_rules_summary.md @@ -1,5 +1,3 @@ -- Allow you to manage access to interactions between Buildkite resources. +- Grant access between Buildkite resources that would normally be restricted by [cluster](/docs/clusters/overview), [visibility](/docs/pipelines/public-pipelines), or [permissions](/docs/team-management/permissions). - Allow an action (for example, triggering a build) between a source resource (for example, a pipeline) and a target resource (for example, another pipeline) across your Buildkite organization. - -- Are used to grant access between resources that would normally be restricted by [cluster](/docs/clusters/overview), [visibility](/docs/pipelines/public-pipelines), or [permissions](/docs/team-management/permissions). From 472eaa5b9c9d813946dd4d38bf5805d026268bc6 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Wed, 4 Sep 2024 15:06:07 +1000 Subject: [PATCH 047/369] More intro tweaks and fix another typo in GraphQL query. --- pages/apis/rest_api/rules.md | 2 +- pages/pipelines/rules.md | 2 +- pages/pipelines/rules/manage.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pages/apis/rest_api/rules.md b/pages/apis/rest_api/rules.md index 4efe4f9a77..f786c32a42 100644 --- a/pages/apis/rest_api/rules.md +++ b/pages/apis/rest_api/rules.md @@ -4,7 +4,7 @@ The rules API lets you create and manage rules in your organization. ## Rules -[Rules](/docs/pipelines/rules) do the following: +[_Rules_](/docs/pipelines/rules) is a Buildkite feature that can do the following: <%= render_markdown partial: 'pipelines/rules_summary' %> diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 33d50bcc72..6438ac712c 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -1,6 +1,6 @@ # Rules overview -Rules do the following: +_Rules_ is a Buildkite feature that can do the following: <%= render_markdown partial: 'pipelines/rules_summary' %> diff --git a/pages/pipelines/rules/manage.md b/pages/pipelines/rules/manage.md index 6a90c4edc8..094b8c5bdd 100644 --- a/pages/pipelines/rules/manage.md +++ b/pages/pipelines/rules/manage.md @@ -116,7 +116,7 @@ where: ```graphql query getCurrentUsersOrgs { viewer { - organization { + organizations { edges { node { name From 0791802d532098c8247098c0cfbc888e6780c881 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 5 Sep 2024 10:17:34 +1000 Subject: [PATCH 048/369] Add 'early access' note to top of Rules overview page. --- pages/pipelines/rules.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 6438ac712c..a6e37e4d83 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -4,6 +4,9 @@ _Rules_ is a Buildkite feature that can do the following: <%= render_markdown partial: 'pipelines/rules_summary' %> +> 📘 +> The _rules_ feature is currently in development and is enabled on an opt-in basis for early access. To enable rules your organization, please contact Buildkite's [Support team](https://buildkite.com/support). + ## Rule types Buildkite Pipelines supports two types of rules that allow one pipeline build to: From b8216993d266d471e1750c0e597c78aa3512a309 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 5 Sep 2024 10:34:13 +1000 Subject: [PATCH 049/369] Fix terminology around hash > JSON object, and tweak early access note for rules slightly. --- pages/apis/rest_api/builds.md | 4 ++-- pages/apis/rest_api/rules.md | 2 +- pages/pipelines/rules.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pages/apis/rest_api/builds.md b/pages/apis/rest_api/builds.md index 8c1c545896..8573e27f74 100644 --- a/pages/apis/rest_api/builds.md +++ b/pages/apis/rest_api/builds.md @@ -517,12 +517,12 @@ Optional [request body properties](/docs/api#request-body-properties):
typeThe rule type. Must match one of the [available rule types](/docs/pipelines/rules/overview#available-rule-types)
+
The rule type. Must match one of the [available rule types](/docs/pipelines/rules#available-rule-types)
Example: "pipeline.trigger_build.pipeline"
typeThe rule type. Must match one of the [available rule types](/docs/pipelines/rules#available-rule-types)
- Example: "pipeline.trigger_build.pipeline"
The rule type. Must match one of the available rule types.
+ Example: +
    +
  • "pipeline.trigger_build.pipeline"
    or
  • +
  • "pipeline.artifacts_read.pipeline"
  • +
value
- + - + diff --git a/pages/apis/rest_api/rules.md b/pages/apis/rest_api/rules.md index f786c32a42..c2eed2e7c3 100644 --- a/pages/apis/rest_api/rules.md +++ b/pages/apis/rest_api/rules.md @@ -139,7 +139,7 @@ Required [request body properties](/docs/api#request-body-properties): - diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index a6e37e4d83..8828cd7314 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -5,7 +5,7 @@ _Rules_ is a Buildkite feature that can do the following: <%= render_markdown partial: 'pipelines/rules_summary' %> > 📘 -> The _rules_ feature is currently in development and is enabled on an opt-in basis for early access. To enable rules your organization, please contact Buildkite's [Support team](https://buildkite.com/support). +> The _rules_ feature is currently in development, and is enabled on an opt-in basis for early access. To enable rules your organization, please contact Buildkite's [Support team](https://buildkite.com/support). ## Rule types From 0d8bab016d1a698a08e733c844771929ce3b3bf9 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 5 Sep 2024 10:50:23 +1000 Subject: [PATCH 050/369] Fix typo. --- pages/pipelines/rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 8828cd7314..74a56d247d 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -5,7 +5,7 @@ _Rules_ is a Buildkite feature that can do the following: <%= render_markdown partial: 'pipelines/rules_summary' %> > 📘 -> The _rules_ feature is currently in development, and is enabled on an opt-in basis for early access. To enable rules your organization, please contact Buildkite's [Support team](https://buildkite.com/support). +> The _rules_ feature is currently in development, and is enabled on an opt-in basis for early access. To enable rules for your organization, please contact Buildkite's [Support team](https://buildkite.com/support). ## Rule types From 82bdc4559a3052a263cd82d0a5632ea5562d36a8 Mon Sep 17 00:00:00 2001 From: Ben McNicholl Date: Thu, 5 Sep 2024 10:56:13 +1000 Subject: [PATCH 051/369] Update pages/pipelines/waterfall.md Co-authored-by: Giles Gas --- pages/pipelines/waterfall.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/waterfall.md b/pages/pipelines/waterfall.md index ce1ce33c59..40f9cf473a 100644 --- a/pages/pipelines/waterfall.md +++ b/pages/pipelines/waterfall.md @@ -31,6 +31,6 @@ Group, matrix and parallel steps are shown with nested rows underneath a 'parent <%= image "waterfall-view-parent-row.png", alt: "Image showing an example of a parent row and its children in a waterfall chart" %> -> 📘 Build time discrepency vs waterfall view +> 📘 Build time discrepancies in the waterfall view > Although canceled jobs will show as a blank line in the waterfall view, they still contribute to the total build time. If a job ran for 20 minutes and was then canceled, that job will appear as a blank line in the waterfall view, but contribute 20 minutes to the total build time. From 479986abd8ec97a9dffea77e964da12a43884257 Mon Sep 17 00:00:00 2001 From: Ben McNicholl Date: Thu, 5 Sep 2024 10:58:30 +1000 Subject: [PATCH 052/369] Update pages/pipelines/waterfall.md Co-authored-by: Giles Gas --- pages/pipelines/waterfall.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/waterfall.md b/pages/pipelines/waterfall.md index 40f9cf473a..9146e09593 100644 --- a/pages/pipelines/waterfall.md +++ b/pages/pipelines/waterfall.md @@ -32,5 +32,5 @@ Group, matrix and parallel steps are shown with nested rows underneath a 'parent <%= image "waterfall-view-parent-row.png", alt: "Image showing an example of a parent row and its children in a waterfall chart" %> > 📘 Build time discrepancies in the waterfall view -> Although canceled jobs will show as a blank line in the waterfall view, they still contribute to the total build time. If a job ran for 20 minutes and was then canceled, that job will appear as a blank line in the waterfall view, but contribute 20 minutes to the total build time. +> Although canceled jobs appear as a blank line in the waterfall view, their duration still contributes to the total build time. For example, if a job ran for 20 minutes and was then canceled, that job will appear as a blank line in the waterfall view, but contributes 20 minutes to the total build time. From 5a327059f72fc86725a4175a4cbf2301b32c51ea Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 5 Sep 2024 11:59:57 +1000 Subject: [PATCH 053/369] Minor sentence tweak to make it read less clunkily. --- pages/pipelines/_rules_summary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/_rules_summary.md b/pages/pipelines/_rules_summary.md index dda32b90b6..52428b89d3 100644 --- a/pages/pipelines/_rules_summary.md +++ b/pages/pipelines/_rules_summary.md @@ -1,3 +1,3 @@ - Grant access between Buildkite resources that would normally be restricted by [cluster](/docs/clusters/overview), [visibility](/docs/pipelines/public-pipelines), or [permissions](/docs/team-management/permissions). -- Allow an action (for example, triggering a build) between a source resource (for example, a pipeline) and a target resource (for example, another pipeline) across your Buildkite organization. +- Allows an action between a source resource and a target resource across your Buildkite organization. For example, allowing one pipeline's builds to trigger another pipeline's builds. From e91b17ae28ed825b035182ce2927fea7145e5947 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 6 Sep 2024 10:52:49 +1000 Subject: [PATCH 054/369] Remove obsolete checks. --- .../graphql_api_content/nav_data_spec.rb | 65 ------------------- 1 file changed, 65 deletions(-) diff --git a/spec/scripts/graphql_api_content/nav_data_spec.rb b/spec/scripts/graphql_api_content/nav_data_spec.rb index 0db680dc79..771ca40774 100644 --- a/spec/scripts/graphql_api_content/nav_data_spec.rb +++ b/spec/scripts/graphql_api_content/nav_data_spec.rb @@ -294,71 +294,6 @@ it "generates nav data correctly" do expect(generate_graphql_nav_data(type_sets)).to eq( [ - { - "name" => "Overview", - "path" => "apis/graphql-api" - }, - { - "name" => "Console and CLI tutorial", - "path" => "apis/graphql/graphql-tutorial" - }, - { - "name" => "Cookbook", - "children" => [ - { - "name" => "Overview", - "path" => "apis/graphql/graphql-cookbook" - }, - { - "name" => "Agents", - "path" => "apis/graphql/cookbooks/agents" - }, - { - "name" => "Artifacts", - "path" => "apis/graphql/cookbooks/artifacts" - }, - { - "name" => "Builds", - "path" => "apis/graphql/cookbooks/builds" - }, - { - "name" => "Clusters", - "path" => "apis/graphql/cookbooks/clusters" - }, - { - "name" => "Jobs", - "path" => "apis/graphql/cookbooks/jobs" - }, - { - "name" => "Packages", - "path" => "apis/graphql/cookbooks/packages" - }, - { - "name" => "Pipelines", - "path" => "apis/graphql/cookbooks/pipelines" - }, - { - "name" => "Pipeline templates", - "path" => "apis/graphql/cookbooks/pipeline-templates" - }, - { - "name" => "Rules", - "path" => "apis/graphql/cookbooks/rules" - }, - { - "name" => "Organizations", - "path" => "apis/graphql/cookbooks/organizations" - }, - { - "name" => "Teams", - "path" => "apis/graphql/cookbooks/teams" - } - ] - }, - { - "name" => "Limits", - "path" => "apis/graphql/graphql-resource-limits" - }, { "name" => "Queries", "children" => [ From 864fd954aa8e410638eb5ae899c09064fd939a40 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 6 Sep 2024 11:54:12 +1000 Subject: [PATCH 055/369] Improve landing page wording for Test Engine. --- data/nav.yml | 1 - pages/test_analytics.md | 15 ++++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/data/nav.yml b/data/nav.yml index 08760aca53..e75659a11b 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -692,7 +692,6 @@ path: apis/graphql/cookbooks/teams - name: Limits path: apis/graphql/graphql-resource-limits - - name: "Webhooks" children: - name: "Overview" diff --git a/pages/test_analytics.md b/pages/test_analytics.md index bde6c54a6a..f72d80fb3b 100644 --- a/pages/test_analytics.md +++ b/pages/test_analytics.md @@ -2,20 +2,21 @@ template: "landing_page" --- -# Buildkite Test Analytics +# Buildkite Test Engine -Where Buildkite Pipelines help you automate your build pipelines, -Test Analytics helps you track and analyze the steps in that pipeline that involve tests: +Test Engine is a product that helps you track and analyze the steps in a CI/CD pipelines, which involves: -- Ship code to production faster by optimizing test suites -- Works with any continuous integration -- Identify, fix, and monitor test suite performance -- Track, improve, and monitor test suite reliability +- Shipping code to production faster by optimizing test suites. +- Working with [Buildkite Pipelines](/docs/pipelines), as well as any other CI/CD applications. +- Identifying, fixing, and monitoring test suite performance. +- Tracking, improving, and monitoring test suite reliability. <%= image "overview.png", width: 975, height: 205, alt: "Screenshot of test suite trend showing five metrics over 28 days" %> ## Get started +Run through the **Getting started** section of the Test Engine docs, beginning with [Configuring test suites](/docs/test-analytics/test-suites) for an overview of Test Engine's concepts and functionality, followed by the appropriate test collector for project's langauge: +
From 9e09b7ccab0f8e1597349569b12113a5cbf9bad4 Mon Sep 17 00:00:00 2001 From: Naufan Rizal Date: Fri, 6 Sep 2024 14:49:41 +1200 Subject: [PATCH 056/369] Extract test splitting env var into a yaml --- data/content/test_splitting_env.schema.yaml | 53 +++++++++++++++ data/content/test_splitting_env.yaml | 57 ++++++++++++++++ pages/test_analytics/test_splitting.md | 72 +++++++++++++++------ 3 files changed, 162 insertions(+), 20 deletions(-) create mode 100644 data/content/test_splitting_env.schema.yaml create mode 100644 data/content/test_splitting_env.yaml diff --git a/data/content/test_splitting_env.schema.yaml b/data/content/test_splitting_env.schema.yaml new file mode 100644 index 0000000000..1b94a0606f --- /dev/null +++ b/data/content/test_splitting_env.schema.yaml @@ -0,0 +1,53 @@ +type: object +properties: + predefined: + type: array + items: + type: object + properties: + name: + type: string + desc: + type: array + items: + type: string + required: + - name + - desc + mandatory: + type: array + items: + type: object + properties: + name: + type: string + desc: + type: array + items: + type: string + required: + - name + - desc + optional: + type: array + items: + type: object + properties: + name: + type: string + desc: + type: array + items: + type: string + default: + type: + - string + - integer + - boolean + required: + - name + - desc +required: + - predefined + - mandatory + - optional diff --git a/data/content/test_splitting_env.yaml b/data/content/test_splitting_env.yaml new file mode 100644 index 0000000000..48cd7ed914 --- /dev/null +++ b/data/content/test_splitting_env.yaml @@ -0,0 +1,57 @@ +predefined: + - name: BUILDKITE_BUILD_ID + desc: + - The UUID of the pipeline build. Test Splitter uses this UUID along with `BUILDKITE_STEP_ID` to uniquely identify the test plan. + - name: BUILDKITE_JOB_ID + desc: + - The UUID of the job in the pipeline's build. + - name: BUILDKITE_ORGANIZATION_SLUG + desc: + - The slug of your Buildkite organization. + - name: BUILDKITE_PARALLEL_JOB + desc: + - The index number of a parallel job created from a parallel build step. + - Ensure you configure `parallelism` in your pipeline definition. Learn more about parallel build steps in [Concurrency and parallelism](https://buildkite.com/docs/pipelines/controlling-concurrency#concurrency-and-parallelism). + - name: BUILDKITE_PARALLEL_JOB_COUNT + desc: + - The total number of parallel jobs created from a parallel build step. + - Ensure you configure `parallelism` in your pipeline definition. Learn more about parallel build steps in [Concurrency and parallelism](https://buildkite.com/docs/pipelines/controlling-concurrency#concurrency-and-parallelism). + - name: BUILDKITE_STEP_ID + desc: + - The UUID of the step group in the pipeline build. Test Splitter uses this UUID along with `BUILDKITE_BUILD_ID` to uniquely identify the test plan. + +mandatory: + - name: BUILDKITE_SPLITTER_API_ACCESS_TOKEN + desc: + - Buildkite API access token with `read_suites`, `read_test_plan`, and `write_test_plan` scopes. You can create an [API access token](https://buildkite.com/user/api-access-tokens) from **Personal Settings** > **API Access Tokens** in the Buildkite interface. + - name: BUILDKITE_SPLITTER_SUITE_SLUG + desc: + - The slug of your Buildkite Test Analytics test suite. You can find the suite slug in the url for your test suite. + - "For example, the slug for the url: `https://buildkite.com/organizations/my-organization/analytics/suites/my-suite` is `my-suite`." + +optional: + - name: BUILDKITE_SPLITTER_DEBUG_ENABLED + default: false + desc: + - A flag to enable more verbose logging. + - name: BUILDKITE_SPLITTER_RETRY_COUNT + default: 0 + desc: + - The number of retries permitted. Test Splitter runs the test command defined in `BUILDKITE_SPLITTER_TEST_CMD`, and retries only the failing tests for a maximum of `BUILDKITE_SPLITTER_RETRY_COUNT` times. For RSpec, the Test Splitter runs `BUILDKITE_SPLITTER_TEST_CMD` with `--only-failures` as the retry command. + - name: BUILDKITE_SPLITTER_SPLIT_BY_EXAMPLE + default: false + desc: + - A flag to enable split by example. When this option is `true`, the Test Splitter will split the execution of slow test files over multiple partitions. + - name: BUILDKITE_SPLITTER_TEST_CMD + default: bundle exec rspec {{testExamples}} + desc: + - The test command to run your tests. The Test Splitter will replace and populate the `{{testExamples}}` placeholder with the test plan. + - name: BUILDKITE_SPLITTER_TEST_FILE_EXCLUDE_PATTERN + desc: + - The glob pattern to exclude certain test files or directories. The exclusion will be applied after discovering the test files using a pattern configured with `BUILDKITE_SPLITTER_TEST_FILE_PATTERN`. + - _This option accepts the pattern syntax supported by the [zzglob](https://github.com/DrJosh9000/zzglob?tab=readme-ov-file#pattern-syntax) library._ + - name: BUILDKITE_SPLITTER_TEST_FILE_PATTERN + default: spec/**/*_spec.rb + desc: + - The glob pattern to discover test files. You can exclude certain test files or directories from the discovered test files using a pattern that can be configured with `BUILDKITE_SPLITTER_TEST_FILE_EXCLUDE_PATTERN`. + - _This option accepts the pattern syntax supported by the [zzglob](https://github.com/DrJosh9000/zzglob?tab=readme-ov-file#pattern-syntax) library._ diff --git a/pages/test_analytics/test_splitting.md b/pages/test_analytics/test_splitting.md index 82afe827aa..da78ad0ac3 100644 --- a/pages/test_analytics/test_splitting.md +++ b/pages/test_analytics/test_splitting.md @@ -24,14 +24,22 @@ The Test Splitter tool uses a number of [predefined](#predefined-environment-var By default, the following predefined environment variables are available to your testing environment and do not need any further configuration. If, however, you use Docker or some other type of containerization tool to run your tests, and you wish to use these predefined environment variables in these tests, you may need to expose these environment variables to your containers. -| Environment Variable | Description| -| -------------------- | ----------- | -| `BUILDKITE_BUILD_ID` | The UUID of the pipeline build. Test Splitter uses this UUID along with `BUILDKITE_STEP_ID` to uniquely identify the test plan. | -| `BUILDKITE_JOB_ID` | The UUID of the job in the pipeline's build. | -| `BUILDKITE_ORGANIZATION_SLUG` | The slug of your Buildkite organization. | -| `BUILDKITE_PARALLEL_JOB` | The index number of a parallel job created from a parallel build step.
Ensure you configure `parallelism` in your pipeline definition. Learn more about parallel build steps in [Concurrency and parallelism](https://buildkite.com/docs/pipelines/controlling-concurrency#concurrency-and-parallelism). | -| `BUILDKITE_PARALLEL_JOB_COUNT` | The total number of parallel jobs created from a parallel build step.
Ensure you configure `parallelism` in your pipeline definition. Learn more about parallel build steps in [Concurrency and parallelism](https://buildkite.com/docs/pipelines/controlling-concurrency#concurrency-and-parallelism). | -| `BUILDKITE_STEP_ID` | The UUID of the step group in the pipeline build. Test Splitter uses this UUID along with `BUILDKITE_BUILD_ID` to uniquely identify the test plan. +
authorA hash with a "name" and "email" key to show who created this build.
Default value: the user making the API request.
authorA JSON object with a "name" and "email" key to show who created this build.
Default value: the user making the API request.
clean_checkoutForce the agent to remove any existing build directory and perform a fresh checkout.
Default value: false.
envEnvironment variables to be made available to the build.
Default value: {}.
ignore_pipeline_branch_filtersRun the build regardless of the pipeline's branch filtering rules. Step branch filtering rules will still apply.
Default value: false.
messageMessage for the build.
Example: "Testing all the things \:rocket\:"
meta_dataA hash of meta-data to make available to the build.
Default value: {}.
meta_dataA JSON object of meta-data to make available to the build.
Default value: {}.
pull_request_base_branchFor a pull request build, the base branch of the pull request.
Example: "main"
pull_request_idFor a pull request build, the pull request number.
Example: 42
pull_request_repositoryFor a pull request build, the git repository of the pull request.
Example: "git://github.com/my-org/my-repo.git"
valueA hash containing the value fields for the rule.
+
A JSON object containing the value fields for the rule.
Example: {"source_pipeline_uuid": "16f3b56f-4934-4546-923c-287859851332", "target_pipeline_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac"}
+ + <% TEST_SPLITTING_ENV['predefined'].each do |var| %> + + + + + <% end %> + +
+ <%= var['name'] %> # + + <% var['desc'].each do |d| %> + <%= render_markdown(text: d) %> + <% end %> +
@@ -39,10 +47,22 @@ By default, the following predefined environment variables are available to your The following mandatory environment variables must be set. -| Environment Variable | Description | -| -------------------- | ----------- | -| `BUILDKITE_SPLITTER_API_ACCESS_TOKEN ` | Buildkite API access token with `read_suites`, `read_test_plan`, and `write_test_plan` scopes. You can create an [API access token](https://buildkite.com/user/api-access-tokens) from **Personal Settings** > **API Access Tokens** in the Buildkite interface. | -| `BUILDKITE_SPLITTER_SUITE_SLUG` | The slug of your Buildkite Test Analytics test suite. You can find the suite slug in the url for your test suite. For example, the slug for the url: `https://buildkite.com/organizations/my-organization/analytics/suites/my-suite` is `my-suite` | + + + <% TEST_SPLITTING_ENV['mandatory'].each do |var| %> + + + + + <% end %> + +
+ <%= var['name'] %> # + + <% var['desc'].each do |d| %> + <%= render_markdown(text: d) %> + <% end %> +
@@ -50,14 +70,26 @@ The following mandatory environment variables must be set. The following optional environment variables can also be used to configure the Test Splitter's behavior. -| Environment Variable | Default Value | Description | -| ---- | ---- | ----------- | -| `BUILDKITE_SPLITTER_DEBUG_ENABLED` | `false` | A flag to enable more verbose logging. | -| `BUILDKITE_SPLITTER_RETRY_COUNT` | `0` | The number of retries permitted. Test Splitter runs the test command defined in `BUILDKITE_SPLITTER_TEST_CMD`, and retries only the failing tests for a maximum of `BUILDKITE_SPLITTER_RETRY_COUNT` times. For RSpec, the Test Splitter runs `BUILDKITE_SPLITTER_TEST_CMD` with `--only-failures` as the retry command. | -| `BUILDKITE_SPLITTER_SPLIT_BY_EXAMPLE` | `false` | A flag to enable split by example. When this option is `true`, the Test Splitter will split the execution of slow test files over multiple partitions. | -| `BUILDKITE_SPLITTER_TEST_CMD` | `bundle exec rspec {{testExamples}}` | The test command to run your tests. The Test Splitter will replace and populate the `{{testExamples}}` placeholder with the test plan. | -| `BUILDKITE_SPLITTER_TEST_FILE_EXCLUDE_PATTERN` | - | The glob pattern to exclude certain test files or directories. The exclusion will be applied after discovering the test files using a pattern configured with `BUILDKITE_SPLITTER_TEST_FILE_PATTERN`.
_This option accepts the pattern syntax supported by the [zzglob](https://github.com/DrJosh9000/zzglob?tab=readme-ov-file#pattern-syntax) library._ | -| `BUILDKITE_SPLITTER_TEST_FILE_PATTERN` | `spec/**/*_spec.rb` | The glob pattern to discover test files. You can exclude certain test files or directories from the discovered test files using a pattern that can be configured with `BUILDKITE_SPLITTER_TEST_FILE_EXCLUDE_PATTERN`.
_This option accepts the pattern syntax supported by the [zzglob](https://github.com/DrJosh9000/zzglob?tab=readme-ov-file#pattern-syntax) library._ | + + + <% TEST_SPLITTING_ENV['optional'].each do |var| %> + + + + + <% end %> + +
+ <%= var['name'] %> # +

+ Default: + <%= var['default'] %> +

+
+ <% var['desc'].each do |d| %> + <%= render_markdown(text: d) %> + <% end %> +
### Update the pipeline step From 39869444757d9cc0dd6ac65f521ddbd9ad2d4777 Mon Sep 17 00:00:00 2001 From: Mark Wolfe Date: Fri, 6 Sep 2024 14:01:08 +1000 Subject: [PATCH 057/369] Update the docs site with the changes from the v3.80.0 agent release --- pages/agent/v3/help/_annotate.md | 2 +- pages/agent/v3/help/_bootstrap.md | 1 + pages/agent/v3/help/_pipeline_upload.md | 1 + pages/agent/v3/help/_start.md | 2 ++ pages/agent/v3/help/_tool_sign.md | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pages/agent/v3/help/_annotate.md b/pages/agent/v3/help/_annotate.md index b33c649c19..908a776370 100644 --- a/pages/agent/v3/help/_annotate.md +++ b/pages/agent/v3/help/_annotate.md @@ -56,7 +56,7 @@ $ ./script/dynamic_annotation_generator | buildkite-agent annotate --style "succ --context value #

The context of the annotation used to differentiate this annotation from others
Environment variable: $BUILDKITE_ANNOTATION_CONTEXT

--style value #

The style of the annotation (`success`, `info`, `warning` or `error`)
Environment variable: $BUILDKITE_ANNOTATION_STYLE

--append #

Append to the body of an existing annotation
Environment variable: $BUILDKITE_ANNOTATION_APPEND

---priority value #

Priority of the annotation (1 to 10). By default annotations have a priority of 3. Annotations with a priority of 10 will be shown first, and annotations with a priority of 1 will be shown last. (default: 0)
Environment variable: $BUILDKITE_ANNOTATION_PRIORITY

+--priority value #

The priority of the annotation (`1` to `10`). Annotations with a priority of `10` are shown first, while annotations with a priority of `1` are shown last. (default: 3)
Environment variable: $BUILDKITE_ANNOTATION_PRIORITY

--job value #

Which job should the annotation come from
Environment variable: $BUILDKITE_JOB_ID

--agent-access-token value #

The access token used to identify the agent
Environment variable: $BUILDKITE_AGENT_ACCESS_TOKEN

--endpoint value #

The Agent API endpoint (default: "https://agent.buildkite.com/v3")
Environment variable: $BUILDKITE_AGENT_ENDPOINT

diff --git a/pages/agent/v3/help/_bootstrap.md b/pages/agent/v3/help/_bootstrap.md index 00687c18c8..00d415841f 100644 --- a/pages/agent/v3/help/_bootstrap.md +++ b/pages/agent/v3/help/_bootstrap.md @@ -103,6 +103,7 @@ $ buildkite-agent bootstrap --build-path builds --redacted-vars value #

Pattern of environment variable names containing sensitive values (default: "*_PASSWORD", "*_SECRET", "*_TOKEN", "*_PRIVATE_KEY", "*_ACCESS_KEY", "*_SECRET_KEY", "*_CONNECTION_STRING")
Environment variable: $BUILDKITE_REDACTED_VARS

--strict-single-hooks #

Enforces that only one checkout hook, and only one command hook, can be run
Environment variable: $BUILDKITE_STRICT_SINGLE_HOOKS

--kubernetes-exec #

This is intended to be used only by the Buildkite k8s stack (github.com/buildkite/agent-stack-k8s); it enables a Unix socket for transporting logs and exit statuses between containers in a pod
Environment variable: $BUILDKITE_KUBERNETES_EXEC

+--trace-context-encoding value #

Sets the inner encoding for BUILDKITE_TRACE_CONTEXT. Must be either json or gob (default: "gob")
Environment variable: $BUILDKITE_TRACE_CONTEXT_ENCODING

diff --git a/pages/agent/v3/help/_pipeline_upload.md b/pages/agent/v3/help/_pipeline_upload.md index 0066ebbc4d..930fef94bf 100644 --- a/pages/agent/v3/help/_pipeline_upload.md +++ b/pages/agent/v3/help/_pipeline_upload.md @@ -58,6 +58,7 @@ $ ./script/dynamic_step_generator | buildkite-agent pipeline upload --reject-secrets #

When true, fail the pipeline upload early if the pipeline contains secrets
Environment variable: $BUILDKITE_AGENT_PIPELINE_UPLOAD_REJECT_SECRETS

--jwks-file value #

Path to a file containing a JWKS. Passing this flag enables pipeline signing
Environment variable: $BUILDKITE_AGENT_JWKS_FILE

--jwks-key-id value #

The JWKS key ID to use when signing the pipeline. Required when using a JWKS
Environment variable: $BUILDKITE_AGENT_JWKS_KEY_ID

+--signing-aws-kms-key value #

The AWS KMS key identifier which is used to sign pipelines.
Environment variable: $BUILDKITE_AGENT_AWS_KMS_KEY

--debug-signing #

Enable debug logging for pipeline signing. This can potentially leak secrets to the logs as it prints each step in full before signing. Requires debug logging to be enabled
Environment variable: $BUILDKITE_AGENT_DEBUG_SIGNING

--agent-access-token value #

The access token used to identify the agent
Environment variable: $BUILDKITE_AGENT_ACCESS_TOKEN

--endpoint value #

The Agent API endpoint (default: "https://agent.buildkite.com/v3")
Environment variable: $BUILDKITE_AGENT_ENDPOINT

diff --git a/pages/agent/v3/help/_start.md b/pages/agent/v3/help/_start.md index 8acb63c1a5..00cfe3c249 100644 --- a/pages/agent/v3/help/_start.md +++ b/pages/agent/v3/help/_start.md @@ -103,6 +103,7 @@ $ buildkite-agent start --token xxx --verification-jwks-file value #

Path to a file containing a JSON Web Key Set (JWKS), used to verify job signatures.
Environment variable: $BUILDKITE_AGENT_VERIFICATION_JWKS_FILE

--signing-jwks-file value #

Path to a file containing a signing key. Passing this flag enables pipeline signing for all pipelines uploaded by this agent. For hmac-sha256, the raw file content is used as the shared key
Environment variable: $BUILDKITE_AGENT_SIGNING_JWKS_FILE

--signing-jwks-key-id value #

The JWKS key ID to use when signing the pipeline. If omitted, and the signing JWKS contains only one key, that key will be used.
Environment variable: $BUILDKITE_AGENT_SIGNING_JWKS_KEY_ID

+--signing-aws-kms-key value #

The KMS KMS key ID, or key alias used when signing and verifying the pipeline.
Environment variable: $BUILDKITE_AGENT_SIGNING_AWS_KMS_KEY

--debug-signing #

Enable debug logging for pipeline signing. This can potentially leak secrets to the logs as it prints each step in full before signing. Requires debug logging to be enabled
Environment variable: $BUILDKITE_AGENT_DEBUG_SIGNING

--verification-failure-behavior value #

The behavior when a job is received without a signature. One of: [block warn]. Defaults to block (default: "block")
Environment variable: $BUILDKITE_AGENT_JOB_VERIFICATION_NO_SIGNATURE_BEHAVIOR

--disable-warnings-for value #

A list of warning IDs to disable
Environment variable: $BUILDKITE_AGENT_DISABLE_WARNINGS_FOR

@@ -118,6 +119,7 @@ $ buildkite-agent start --token xxx --redacted-vars value #

Pattern of environment variable names containing sensitive values (default: "*_PASSWORD", "*_SECRET", "*_TOKEN", "*_PRIVATE_KEY", "*_ACCESS_KEY", "*_SECRET_KEY", "*_CONNECTION_STRING")
Environment variable: $BUILDKITE_REDACTED_VARS

--strict-single-hooks #

Enforces that only one checkout hook, and only one command hook, can be run
Environment variable: $BUILDKITE_STRICT_SINGLE_HOOKS

--kubernetes-exec #

This is intended to be used only by the Buildkite k8s stack (github.com/buildkite/agent-stack-k8s); it enables a Unix socket for transporting logs and exit statuses between containers in a pod
Environment variable: $BUILDKITE_KUBERNETES_EXEC

+--trace-context-encoding value #

Sets the inner encoding for BUILDKITE_TRACE_CONTEXT. Must be either json or gob (default: "gob")
Environment variable: $BUILDKITE_TRACE_CONTEXT_ENCODING

--tags-from-ec2 #

Include the host's EC2 meta-data as tags (instance-id, instance-type, and ami-id)
Environment variable: $BUILDKITE_AGENT_TAGS_FROM_EC2

--tags-from-gcp #

Include the host's Google Cloud instance meta-data as tags (instance-id, machine-type, preemptible, project-id, region, and zone)
Environment variable: $BUILDKITE_AGENT_TAGS_FROM_GCP

diff --git a/pages/agent/v3/help/_tool_sign.md b/pages/agent/v3/help/_tool_sign.md index a2b74b184d..91628e37d7 100644 --- a/pages/agent/v3/help/_tool_sign.md +++ b/pages/agent/v3/help/_tool_sign.md @@ -61,6 +61,7 @@ $ cat pipeline.yml | buildkite-agent tool sign \ --no-confirm #

Show confirmation prompts before updating the pipeline with the GraphQL API.
Environment variable: $BUILDKITE_TOOL_SIGN_NO_CONFIRM

--jwks-file value #

Path to a file containing a JWKS.
Environment variable: $BUILDKITE_AGENT_JWKS_FILE

--jwks-key-id value #

The JWKS key ID to use when signing the pipeline. If none is provided and the JWKS file contains only one key, that key will be used.
Environment variable: $BUILDKITE_AGENT_JWKS_KEY_ID

+--signing-aws-kms-key value #

The AWS KMS key identifier which is used to sign pipelines.
Environment variable: $BUILDKITE_AGENT_AWS_KMS_KEY

--debug-signing #

Enable debug logging for pipeline signing. This can potentially leak secrets to the logs as it prints each step in full before signing. Requires debug logging to be enabled
Environment variable: $BUILDKITE_AGENT_DEBUG_SIGNING

--organization-slug value #

The organization slug. Required to connect to the GraphQL API.
Environment variable: $BUILDKITE_ORGANIZATION_SLUG

--pipeline-slug value #

The pipeline slug. Required to connect to the GraphQL API.
Environment variable: $BUILDKITE_PIPELINE_SLUG

From acdd840f848a58effbd5c6377ff2e99da2c89f58 Mon Sep 17 00:00:00 2001 From: lizrabuya <115472349+lizrabuya@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:31:07 +1000 Subject: [PATCH 058/369] v0.0.0 --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e8fb09a31f..a571387831 100644 --- a/package.json +++ b/package.json @@ -21,5 +21,6 @@ "dependencies": { "@docsearch/js": "3", "@hotwired/turbo-rails": "^7.3.0" - } + }, + "version": "0.0.0" } From 682bf41e21942cc1a877b43c5f81a3f50cd64f18 Mon Sep 17 00:00:00 2001 From: lizrabuya <115472349+lizrabuya@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:48:40 +1000 Subject: [PATCH 059/369] Add info on how often a job expiry process is ran --- pages/pipelines/build_timeouts.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pages/pipelines/build_timeouts.md b/pages/pipelines/build_timeouts.md index 51dae6d375..ad6239dd56 100644 --- a/pages/pipelines/build_timeouts.md +++ b/pages/pipelines/build_timeouts.md @@ -38,3 +38,6 @@ By default, jobs are canceled when not picked up for 30 days. This will cause th You can override the default by setting a shorter value in your organization's [**Pipeline Settings**](https://buildkite.com/organizations/~/pipeline-settings) page. Scheduled job limits should not be confused with [scheduled builds](/docs/pipelines/scheduled-builds). A scheduled build's jobs will still go through the [build states](/docs/pipelines/defining-steps#build-states), and the timeout will apply once its individual jobs are in the scheduled state waiting for agents. + +> 📘 Delays in expiring a job +> A job's expiry process is run hourly at 5 minutes past. When the expiry process runs and the job's scheduled expiry was not over at that hour, it will only be expired until the next hour when the process is executed again. \ No newline at end of file From 18795c3822364bcb6418208a2019cd36280b1aeb Mon Sep 17 00:00:00 2001 From: lizrabuya <115472349+lizrabuya@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:58:29 +1000 Subject: [PATCH 060/369] Fix lint errors --- pages/pipelines/build_timeouts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/build_timeouts.md b/pages/pipelines/build_timeouts.md index ad6239dd56..13b21a2178 100644 --- a/pages/pipelines/build_timeouts.md +++ b/pages/pipelines/build_timeouts.md @@ -40,4 +40,4 @@ You can override the default by setting a shorter value in your organization's [ Scheduled job limits should not be confused with [scheduled builds](/docs/pipelines/scheduled-builds). A scheduled build's jobs will still go through the [build states](/docs/pipelines/defining-steps#build-states), and the timeout will apply once its individual jobs are in the scheduled state waiting for agents. > 📘 Delays in expiring a job -> A job's expiry process is run hourly at 5 minutes past. When the expiry process runs and the job's scheduled expiry was not over at that hour, it will only be expired until the next hour when the process is executed again. \ No newline at end of file +> A job's expiry process is run hourly at 5 minutes past. When the expiry process runs and the job's scheduled expiry was not over at that hour, it will only be expired until the next hour when the process is executed again. From 00d76fa8e73354cec889938c140b211db0421d1f Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 6 Sep 2024 15:09:00 +1000 Subject: [PATCH 061/369] Improved wording and messaging for the Buildkite Test Engine landing page. --- data/nav.yml | 31 ++++++++++++++----------------- pages/platform.md | 2 ++ pages/test_analytics.md | 18 +++++++++--------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/data/nav.yml b/data/nav.yml index e75659a11b..d4c6840440 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -484,24 +484,21 @@ path: "packages/background" - name: "Getting started" path: "packages/getting-started" - - name: "Registries" - start_expanded: true - children: - - name: "Manage registries" - path: "packages/manage-registries" - - name: "Private storage" - path: "packages/private-storage" - - name: "Security" - start_expanded: true - children: - - name: "Overview" - path: "packages/security" - - name: "OIDC" - path: "packages/security/oidc" - - name: "Permissions" - path: "packages/security/permissions" + - name: "Registries" + children: + - name: "Manage registries" + path: "packages/manage-registries" + - name: "Private storage" + path: "packages/private-storage" + - name: "Security" + children: + - name: "Overview" + path: "packages/security" + - name: "OIDC" + path: "packages/security/oidc" + - name: "Permissions" + path: "packages/security/permissions" - name: "Package ecosystems" - start_expanded: true children: - name: "Alpine" path: "packages/alpine" diff --git a/pages/platform.md b/pages/platform.md index 80c7b2179d..7cd035a137 100644 --- a/pages/platform.md +++ b/pages/platform.md @@ -3,3 +3,5 @@ template: "landing_page" --- # The Buildkite platform + +The Buildkite platform documentation contains \ No newline at end of file diff --git a/pages/test_analytics.md b/pages/test_analytics.md index f72d80fb3b..05ecc3712f 100644 --- a/pages/test_analytics.md +++ b/pages/test_analytics.md @@ -4,10 +4,12 @@ template: "landing_page" # Buildkite Test Engine -Test Engine is a product that helps you track and analyze the steps in a CI/CD pipelines, which involves: +Scale out your testing across any framework with Buildkite Test Engine. Get more out of fewer tests with performance insights to speed up builds and isolate unreliable tests. -- Shipping code to production faster by optimizing test suites. -- Working with [Buildkite Pipelines](/docs/pipelines), as well as any other CI/CD applications. +Where [Buildkite Pipelines](/docs/pipelines) helps you automate your CI/CD pipelines, Test Engine helps you track and analyze the steps in these pipelines, by: + +- Shipping code to production faster through test suite optimization. +- Working directly with Buildkite Pipelines, as well as other CI/CD applications. - Identifying, fixing, and monitoring test suite performance. - Tracking, improving, and monitoring test suite reliability. @@ -15,7 +17,7 @@ Test Engine is a product that helps you track and analyze the steps in a CI/CD p ## Get started -Run through the **Getting started** section of the Test Engine docs, beginning with [Configuring test suites](/docs/test-analytics/test-suites) for an overview of Test Engine's concepts and functionality, followed by the appropriate test collector for project's langauge: +Run through the 'Getting started' section of these Test Engine docs, beginning with [Configuring test suites](/docs/test-analytics/test-suites) for an overview of Test Engine's concepts and functionality, followed by the appropriate test collector for project's language: @@ -41,13 +43,11 @@ Run through the **Getting started** section of the Test Engine docs, beginning w You can also upload test results by importing [JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml). ->📘 Data retention -> The data uploaded to Test Analytics is stored in S3 and deleted after six months. - ----- +
<%= tiles "test_analytics_features" %> ----- +>📘 Data retention +> The data uploaded to Test Analytics is stored in S3 and deleted after six months. <%= tiles "test_analytics_guides" %> From 60bbac66f0ce1099b529f0deea3215976131f35e Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 6 Sep 2024 15:44:49 +1000 Subject: [PATCH 062/369] Add new messaging to tops of product landing pages, as well as Platform area. --- app/views/landing_page_pipelines/_hero.html.erb | 2 +- pages/packages.md | 8 +++++--- pages/platform.md | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/views/landing_page_pipelines/_hero.html.erb b/app/views/landing_page_pipelines/_hero.html.erb index 4da6d2e746..426b3c983c 100644 --- a/app/views/landing_page_pipelines/_hero.html.erb +++ b/app/views/landing_page_pipelines/_hero.html.erb @@ -2,7 +2,7 @@

Buildkite Pipelines

-

Create fast, secure, and reliable CI/CD with Buildkite Pipelines so you can quickly and confidently ship quality code. We provide the docs, building blocks, and extension points you'll need to build your dream CI/CD workflows. Let's get building! 🚀

+

Create fast, secure, and reliable CI/CD with Buildkite Pipelines so you can quickly and confidently ship quality code. Scale out automated workflows with massive concurrency and flexible compute types on Buildkite Pipelines. Fine-tune each workflow to match your business needs. Buildkite provides the building blocks, docs, and extension points you'll need to build your dream CI/CD workflows. Let's get building! 🚀

diff --git a/pages/packages.md b/pages/packages.md index be5f06f9ad..ddd0c0e8a5 100644 --- a/pages/packages.md +++ b/pages/packages.md @@ -4,11 +4,13 @@ template: "landing_page" # Buildkite Packages -Buildkite Packages is a product that: +Scale out asset management across any ecosystem with Buildkite Packages. Avoid the bottleneck of poorly managed and insecure dependencies. -- Manages artifacts and packages from [Buildkite Pipelines](/docs/pipelines), as well as other CI/CD applications that require artifact management. +Buildkite Packages allows you to: -- Provides registries to store your [packages and other package-like file formats](/docs/packages/background) such as container images and Terraform modules. +- Manage artifacts and packages from [Buildkite Pipelines](/docs/pipelines), as well as other CI/CD applications that require artifact management. + +- Provide registries to store your [packages and other package-like file formats](/docs/packages/background) such as container images and Terraform modules. As well as storing a collection of packages, a registry also surfaces metadata or attributes associated with a package, such as the package's description, version, contents (files and directories), checksum details, distribution type, dependencies, and so on. diff --git a/pages/platform.md b/pages/platform.md index 7cd035a137..565ceb0f98 100644 --- a/pages/platform.md +++ b/pages/platform.md @@ -4,4 +4,6 @@ template: "landing_page" # The Buildkite platform -The Buildkite platform documentation contains \ No newline at end of file +The Buildkite Scale-Out Delivery Platform is an adaptable, composable, and scalable platform with everything platform teams need to build software delivery systems for their businesses—and rapidly deliver value to users. + +The Buildkite platform documentation contains docs for _platform_-level features of Buildkite that can apply to Buildkite [Pipelines](/docs/pipelines), [Test Engine](/docs/test-analytics), as well as [Packages](/docs/packages). From e579d9c7b12dc8020eef60636a2aa6e3ada54c4e Mon Sep 17 00:00:00 2001 From: lizrabuya <115472349+lizrabuya@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:17:51 +1000 Subject: [PATCH 063/369] Add outline for packages migration pages --- data/nav.yml | 8 ++++++++ pages/packages/migrate/from_cloudsmith.md | 7 +++++++ pages/packages/migrate/from_jfrog_artifactory.md | 7 +++++++ pages/packages/migrate/overview.md | 7 +++++++ 4 files changed, 29 insertions(+) create mode 100644 pages/packages/migrate/from_cloudsmith.md create mode 100644 pages/packages/migrate/from_jfrog_artifactory.md create mode 100644 pages/packages/migrate/overview.md diff --git a/data/nav.yml b/data/nav.yml index 605e8cae2e..01df29c146 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -484,6 +484,14 @@ path: "packages/security/oidc" - name: "Permissions" path: "packages/permissions" + - name: Migration + children: + - name: Overview + path: packages/migrate/overview + - name: From JFrog Artifactory + path: packages/migrate/from-jfrog-artifactory + - name: From Cloudsmith + path: packages/migrate/from-cloudsmith - name: "Package ecosystems" start_expanded: true children: diff --git a/pages/packages/migrate/from_cloudsmith.md b/pages/packages/migrate/from_cloudsmith.md new file mode 100644 index 0000000000..2d9b966ce9 --- /dev/null +++ b/pages/packages/migrate/from_cloudsmith.md @@ -0,0 +1,7 @@ +--- +toc: false +--- + +# Migrate from Cloudsmith + +How to migrate your packages from Cloudsmit to Buildkite Packages. diff --git a/pages/packages/migrate/from_jfrog_artifactory.md b/pages/packages/migrate/from_jfrog_artifactory.md new file mode 100644 index 0000000000..b1886cb894 --- /dev/null +++ b/pages/packages/migrate/from_jfrog_artifactory.md @@ -0,0 +1,7 @@ +--- +toc: false +--- + +# Migrate from JFROG Artifactory + +How to migrate your packages from JFROG Artifactory to Buildkite Packages. diff --git a/pages/packages/migrate/overview.md b/pages/packages/migrate/overview.md new file mode 100644 index 0000000000..71fc2872c5 --- /dev/null +++ b/pages/packages/migrate/overview.md @@ -0,0 +1,7 @@ +--- +toc: false +--- + +# Migrate to Buildkite Packages + +Overview on migrating to Buildkite Packages from other package providers. From 6ee2eeb71dab6f924db6208ce990a05b7ee4a2a2 Mon Sep 17 00:00:00 2001 From: Ivanna Lisetska Date: Fri, 6 Sep 2024 15:56:16 -0600 Subject: [PATCH 064/369] Update docs with verification-failure-behavior --- pages/agent/v3/signed_pipelines.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pages/agent/v3/signed_pipelines.md b/pages/agent/v3/signed_pipelines.md index 689af656aa..c32949cecc 100644 --- a/pages/agent/v3/signed_pipelines.md +++ b/pages/agent/v3/signed_pipelines.md @@ -102,6 +102,14 @@ verification-jwks-file= This ensures that whenever those agents upload steps to Buildkite, they'll generate signatures using the private key you generated earlier. It also ensures that those agents verify the signatures of any steps they run, using the public key. +```ini +verification-failure-behavior= +``` + +This setting determines the BuildKite agent’s response when it receives a job without a proper signature. It specifies how strictly the agent should enforce signature verification for incoming jobs. The agent will warn about the missing signature but will still proceed with executing the job. If not explicitly specified, the default behavior is `block`, which will prevent any job without a signature from running, ensuring a secure pipeline environment by default. + + + On instances that verify jobs, add: ```ini From 14b1421bc457ecbafe45b9f7dae6bddaefa8f330 Mon Sep 17 00:00:00 2001 From: buildkite-docs-bot Date: Sat, 7 Sep 2024 00:08:05 +0000 Subject: [PATCH 065/369] Update GraphQL docs --- data/graphql/schema.graphql | 10 ++++++++++ .../graphql/schemas/input_object/rulecreateinput.md | 2 +- pages/apis/graphql/schemas/object/rule.md | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/data/graphql/schema.graphql b/data/graphql/schema.graphql index b01b5ffe32..1b94c00d3a 100644 --- a/data/graphql/schema.graphql +++ b/data/graphql/schema.graphql @@ -8958,6 +8958,11 @@ type Rule implements Node { """ createdBy: User + """ + Description of the rule + """ + description: String + """ Effect for the rule """ @@ -9025,6 +9030,11 @@ input RuleCreateInput { A unique identifier for the client performing the mutation. """ clientMutationId: String + + """ + Description of the rule + """ + description: String organizationId: ID! """ diff --git a/pages/apis/graphql/schemas/input_object/rulecreateinput.md b/pages/apis/graphql/schemas/input_object/rulecreateinput.md index a2b2ac2e69..f59b653b73 100644 --- a/pages/apis/graphql/schemas/input_object/rulecreateinput.md +++ b/pages/apis/graphql/schemas/input_object/rulecreateinput.md @@ -33,6 +33,6 @@ Autogenerated input type of RuleCreate -

clientMutationIdString

A unique identifier for the client performing the mutation.

organizationIdID!

typeString!

Rule type

valueJSON!

Serialised JSON of the attributes for this rule

+

clientMutationIdString

A unique identifier for the client performing the mutation.

descriptionString

Description of the rule

organizationIdID!

typeString!

Rule type

valueJSON!

Serialised JSON of the attributes for this rule

diff --git a/pages/apis/graphql/schemas/object/rule.md b/pages/apis/graphql/schemas/object/rule.md index 645170528d..30ed7aa51d 100644 --- a/pages/apis/graphql/schemas/object/rule.md +++ b/pages/apis/graphql/schemas/object/rule.md @@ -31,7 +31,7 @@ toc: false -

actionRuleAction

Action for the rule

createdByUser

User who created the rule

effectRuleEffect

Effect for the rule

idID!

organizationOrganization

sourceRuleSource

The source for the rule

sourceTypeRuleSourceType

Source type for the rule

targetRuleTarget

The target for the rule

targetTypeRuleTargetType

Target type for the rule

typeString!

The type of rule

uuidID!

The public UUID for the rule

+

actionRuleAction

Action for the rule

createdByUser

User who created the rule

descriptionString

Description of the rule

effectRuleEffect

Effect for the rule

idID!

organizationOrganization

sourceRuleSource

The source for the rule

sourceTypeRuleSourceType

Source type for the rule

targetRuleTarget

The target for the rule

targetTypeRuleTargetType

Target type for the rule

typeString!

The type of rule

uuidID!

The public UUID for the rule

From efac529e783e984c9c3198517dfe0d1d112901f9 Mon Sep 17 00:00:00 2001 From: dimiexplorer Date: Mon, 9 Sep 2024 14:57:39 +1000 Subject: [PATCH 066/369] chore: add git mirror copy --- pages/pipelines/hosted_agents/mac.md | 45 ++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/pages/pipelines/hosted_agents/mac.md b/pages/pipelines/hosted_agents/mac.md index f6da09bf64..c69630a2a7 100644 --- a/pages/pipelines/hosted_agents/mac.md +++ b/pages/pipelines/hosted_agents/mac.md @@ -35,6 +35,9 @@ Updated Xcode versions will be available one week after Apple offers them for do ### Xcode +- Xcode 16.1-Beta +- Xcode 16.0-Beta6 +- Xcode 16.0-Beta5 - Xcode 16.0-Beta4 - Xcode 16.0-Beta3 - Xcode 16.0-Beta2 @@ -55,22 +58,21 @@ Updated Xcode versions will be available one week after Apple offers them for do - iOS 17.5-beta2 - iOS 17.5 - iOS 18.0-beta4 +- iOS 18.1-beta - watchOS 9.4 - watchOS 10.2 - watchOS 10.4 -- watchOS 10.5-beta2 - watchOS 10.5 -- watchOS 11.0 +- watchOS 11.0-beta7 - tvOS 16.4 - tvOS 17.2 - tvOS 17.4 -- tvOS 17.5-beta2 - tvOS 17.5 -- tvOS 18.0 +- tvOS 18.0-beta7 - visionOS 1.0 - visionOS 1.1 - visionOS 1.2 -- visionOS 2.0 +- visionOS 2.0-beta7 ### Other languages and compilers @@ -95,6 +97,7 @@ Updated Xcode versions will be available one week after Apple offers them for do - CocoaPods - Ant - Maven +- Mint - Gradle - Carthage - CMake @@ -149,3 +152,35 @@ Updated Xcode versions will be available one week after Apple offers them for do - libpq - GMP + +## Cache volumes + +_Cache volumes_ are external volumes attached to hosted agent instances. These volumes are attached on a best-effort basis depending on their locality, expiration and current usage, and therefore, should not be relied upon as durable data storage. By default, cache volumes are scoped to a pipeline and are shared between all steps in the pipeline. + +### Git mirror cache + +The Git mirror cache is a special type of cache volume that is used to speed up Git operations by caching the Git repository between builds. This is useful for large repositories that are slow to clone. + +### Enabling Git mirror cache + +To enable Git mirror cache for your hosted agents: + +1. Select **Agents** in the global navigation to access the **Clusters** page. +1. Select the cluster in which to enable Git mirror cache. +1. Select **Cache Storage**, then select the **Settings** tab. +1. Select **Enable Git mirror**, then select **Save cache settings** to enable Git mirrors for the selected hosted cluster. + +Once enabled, the Git mirror cache will be used for all hosted jobs using Git repositories in that cluster. A separate cache volume will be created for each repository. + +<%= image "hosted-agents-git-mirror.png", width: 1760, height: 436, alt: "Hosted agents git mirror setting displayed in the Buildkite UI" %> + +### Deleting Git mirror cache + +Deleting a cache volume may affect the build time for the associated pipelines until the new cache is established. + +To delete a git mirror cache: + +1. Select **Agents** in the global navigation to access the **Clusters** page. +1. Select the cluster in which to delete Git mirror cache. +1. Select **Cache Storage**, then select the **Volumes** tab to view a list of all exiting cache volumes. +1. Select **Delete** next to the Git mirror cache volume you wish to delete. From 0a89fb025814b8abf14251cf3babb838afe52f68 Mon Sep 17 00:00:00 2001 From: dimiexplorer Date: Mon, 9 Sep 2024 15:07:35 +1000 Subject: [PATCH 067/369] chore: update git mirror cache copy --- pages/pipelines/hosted_agents/mac.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pages/pipelines/hosted_agents/mac.md b/pages/pipelines/hosted_agents/mac.md index c69630a2a7..3251e340e5 100644 --- a/pages/pipelines/hosted_agents/mac.md +++ b/pages/pipelines/hosted_agents/mac.md @@ -183,4 +183,5 @@ To delete a git mirror cache: 1. Select **Agents** in the global navigation to access the **Clusters** page. 1. Select the cluster in which to delete Git mirror cache. 1. Select **Cache Storage**, then select the **Volumes** tab to view a list of all exiting cache volumes. -1. Select **Delete** next to the Git mirror cache volume you wish to delete. +1. Select **Delete** for the Git mirror cache volume you wish to remove. +1. Confirm the deletion by selecting **Delete Cache Volume**. From c6d398e3fb90eb0a9df5e0ce9735803377db90ec Mon Sep 17 00:00:00 2001 From: dimiexplorer Date: Mon, 9 Sep 2024 15:26:11 +1000 Subject: [PATCH 068/369] chore: create placeholder hosted-agents-queue-image.png --- .../pipelines/hosted_agents/mac/hosted-agents-queue-image.png | 1 + 1 file changed, 1 insertion(+) create mode 100644 images/docs/pipelines/hosted_agents/mac/hosted-agents-queue-image.png diff --git a/images/docs/pipelines/hosted_agents/mac/hosted-agents-queue-image.png b/images/docs/pipelines/hosted_agents/mac/hosted-agents-queue-image.png new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/images/docs/pipelines/hosted_agents/mac/hosted-agents-queue-image.png @@ -0,0 +1 @@ + From 1b995dfd15745f9d20eb8dc1d93bf9de6eb83fd4 Mon Sep 17 00:00:00 2001 From: dimiexplorer Date: Mon, 9 Sep 2024 15:27:12 +1000 Subject: [PATCH 069/369] chore: add hosted-agents-queue-image.png --- .../mac/hosted-agents-queue-image.png | Bin 1 -> 176129 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/images/docs/pipelines/hosted_agents/mac/hosted-agents-queue-image.png b/images/docs/pipelines/hosted_agents/mac/hosted-agents-queue-image.png index 8b137891791fe96927ad78e64b0aad7bded08bdc..0d6f680671a8bbdf7f79dba7c511cefbd0026a37 100644 GIT binary patch literal 176129 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�@kbq$=2e2r60MDo-H{FSNVQE}54 zz+Bi(hL8usWJ@r00p&LaPfxbWf*!hkbo|f8Y?kdrQOO$#%aTGAHFnO#PmICd7)7U>;{Tgj!k(p_Ob@F*^@V#g;@>PE#RG`ut+U~Sve^QBrN3oVcZ0Uj3UnVAMXVm8H&l&25 z&cseJga2^AoKi!MeEjkEa&@u;LyUAd4rY9r@Y^xwWUW4j1Ur9LOf~V>yLaAhncs50 zg(g>Br4Emha~147R&pW+S*EumM}x8L{Udpqv$N=f9bG?LX^3cRUzZn?+1dqN81#;5*m${TUM~NF6rBN z(~MSwu7Oqq1}C@CIZM7mJz+jZi@5JG#KW6uotfl3EHKrXIo%0r5X2&K6%QYvb$u{h z*4ilMYSh3i($UGb znauXobq?X-II*A02kkg5EvLf^1C*oNIv&$L6;d-~y)xt?V}RM$3Q-ZOdi(xs=dnI5 z{HWGd#UeF%QCjw6Me@z1j?zvV>(BFAJI$e!cjOq>u0tALd|hiHwfelTturInF6a8~ z4GkU#oeyYwuh~c4yUJk2D(_tQ0~RvBQL7~s-# z+R9vWmA(1xZ`6<)L`Z2gOT{Utu3Adxt)8_AhEKUUSo-G6z_YKreQiJcxz@!V+N0!Q zwGgVRm|BR_f!yspeWOQWlz8L@mbP8@^p2MIQ)Fu|x{=|XV^n|sK68Fs@(t$q*;3a& zoXp^^*`lwRXT06VMq@+?NyHm@$H`~_~H_IVoVN%RqUN3EONl3Th z1D{M=#FfXWAEDap!>GrMUC!;p$gWjftSu7w@WW=xr`l|z1SOsoL_t|N1=<3b8kC4a zdMQ)aVsl{g>k!%A`LAr2b13(O%Q1&Uwr+K{xa*|p8$ELTsoD8O=Jn|aH7p7m{Z2on zejo6ma9ib;cnQU<;Wwt98k{|rS=uO&j=tCss&AKG!ZcUh`gVvs8&&H&i)7*Rrtnl6Fg58}fyF<)_I`TL66~csKpb zdE03XM*Y-ccdk=K8BkA9UWYWK_szyxK#s{fvxJq_dJpY6o?W?XB*}_;XS>i_#BNUZ zG)fB<<*32TZRm7!-Xk-ZB&wre8bkT5Im7?qVQvh8pYdADCIG^~fjW0a_&p&-$(2UQ*I%!(k{w#TDaG zEfu=NFPLyJmupb{o_&LIrt{$ydLApka#`%6vPqt9m=||mLPb%}Iq&GEgTky!=;;QR zvY((HURRr)R8ljx?DlX(>UAfiFOm=U{n1Aedv*Jf@E*C=n9-hwD=lH48eVCa+2oc= z<(v!_7$AL>w!X^eQX05F#3jdoH@~__VeT+}aZJ7S!bH%ZaUVMgL62W|PwbTi_7|#M z6$2P~=|{6XJjny@Ot-n$c$2gXsN(K>E-`1a%E&jew<+CZZZR|FSy9iuBg6HEeIu7^ zrmVw%IC-2e&MQMHPTm}sQ%AVys+TL3c$qqt91y;o&K@VFuX}AxUL!dO{D!9--f5p1 z7uQi=56vrgooz9pY2r*1eEz71L{6RU3>C8&i#2BCsQUV();gA*Gp?vyA@Q*E(~}nz z8TQzHk@r+~UItdn@tR^`H!W22V@)-#pXm>Z*}J25`x`go7d zXwmJ*5r4`6=hDK1We;k5TNgbY9}QV0NTAP*ymsA5e^ew-u9M7D7S0#Tk74|_sTIK*)jgHky=#1 z;qB>A=TxjPu3X+`-G1ypH*R6-_>^pBw$#At18y9lKNiu`i7F~%;2D2*8=L;AYg&=Z z*L+}!HM41C4-JZ1z1upPEu-Dfsejh(T$;QjJCw3EoNYK|>N(=6sHi9;VAo9guUc|o zt3R~LK2R&o=C_8nC8t}vj2dDs`E~_!aN7p*^Z}hV$<6T<`L#vM@7MM^q}DiV=2n?= zQnkM}Qj=>e$HTS^&~mZQzcKO0yqoyublYJQLqzRdkz zDnM=vG!$4_tXLcX}Ik-An0Fr`Pjck%58I;vxLZMT6&y%c)&- zlA4jme3BHK)EbN~0S@u*UVd|AJUS|oZBPSC+wCOQ4+45Uu)i%MabAswGF9s_X2XT3 z@ps>G+WOx&lQG^kGs;F$J1@==EWLu%K)+e?E3aSyzka^jZb#AB8-i}JR!S|!m1652 z9$4H+JvvnFo*Ubqyiq-ki*qhH`kL!FW6eOv{78S{gpZ%UC z)n}g(mm9zP{fOLun?;?>B&*N)`Rz}x3*3~xslumWa(IjXp6kJ~)R&16A2`;3WM60aOOux{~Q08=n>{PotRN7|7WD)xJ z&P>cKTD0h+H|I<&{z7&tc4p9fX4HFTW4`R<2iZQ^87k)tHGH}~ye)j@elzih(PP%? z853@!%rovi``KpN!>7q&k^gPI+)lW>T~kAqoz9&ZZ4NLX5}n$asanXkp*Q{F;TE5C z?lz~RtNR_5qiw*n0^`APa&)`i?(jU5(T5?M2HKWzxUH(lo}8Gv3uI zlQw6IeeFeKgV}n&Q+)EG5(0Vp%!ZTlOva&v0Hv(y!kNiJkXX7gt?ygBLF}PVD;Gh+ z(J%khA`g{_J9*6_DQUWbLRy7^9v1Wq$Zr-2y!EC} zwWjuyNwb}Xy{Cgu-sh&?o(amd$nnYv2YHvWnTO*yYy$g+If_PRCL9hEA8}-})~2Qg zal7~~@m|X#TbgZl)$aeuHg-5$>U7e`s|>lgJ6ru1dFVdZ+@z`OA-7R$^D1SJ$O%3V z-HidI;`mP7ol=>^9q6t#pi(Sr3??4OYi?Od{D`?p)RE#Dfz4YqmHm%I9ND5-Y4z6O zlk3sVtG4jgJbV}%ab&aqq736<{mSvd!bSeRVGD^9HFDG-`cT~6!J`p(x29$uezfJT zvcFthWc(rzk%@^XM>ZW@bkyTWMAvkT(?R<}htyLJ1*_LCA3D0lL)l|wHpOE;I^|Hi zbt^tp`S{_8$cEr8nya>)@=!h=(J|dpW2t;;6kRv=WL8uUc*$9@sC2Apy^eCmydCCu z#D}WpU)jsy4~ktrq&jAIJHY49pT&I7K46&=IiAwQ_J48pBlgtw+7@@AG*(=bygc zQ}%wI`@V*Ct+lQTS9P16v}cx@r=k;E)6MU*`%?E6e5B4h)v&T|ftdQ>#=adt{fFly zP}r13ii>;V0uRXsk36O&XfFtBNiHd$zdyprqi9~6Tln-p?{nK}@j;VX-}bcpCU0;4 zWhZoI^jyWBO{Tt`{?pYu=StqqtqFTlYV^oPF3h9b+uS*jzxmAM@XKE|R+bw@ZN#GI z*ZLjXemGUxCbLoO4S{4WS~i~W5A@OTh73*0{diNA#`Y@5c*n zzp>2FujTRYW<%*~F&fx2oKQb#kzUmGrE$n2J-}xaV*SKVEkNB0+XzIk+F6Cc&41|o zDthl#+tG!;INB#(e!GQnJw=^7C6(2;^vFA2>%%h1$S6$Y-;*5`FDw&l`5v`iDB_Ew zpb!F!APYZ}KyO-{)(v!-*5|tpfZ3F!TlgL{CLiz8cLzehe)yojO(NriuJwViEk~6f zoDS#mIV;|Gu43C=<gr4GK>vi?VX(O%C2RIh$kFMAKp)Px0 z2r5?zU0)s9`@`?*S-Z=wrQVvIdvw3+i>cSe3&{_2F@VO;90#kwn#7;dIAoe?s}NK- z@F>06;Yjw#^t`P?8Z&KV+9reSoI48V(-$vZ0PUS$v>!A3F#4l&bn`5A%9UdO7jnD& zAR*w_OEso{%){7~?zdJHpY2aOvvXtOYBCs^@xE|F!nn865@yUG-om-XSHUg~#&R0T zHz)fqL#7{aI6-&4`6@dNvuWEOfbu>Nluc=wde31wvCbvK%_!@S9AhkaK+KcRSJm^&y?ff^Z9Zs8@rc1!!;ERiYtS;Q zow;B{6u(8S;zPVIdhA$XVx!FOx$D0%D|vM14iC}+biHQf$CsbA%&k>4=6l~9bCuQ? z7k@vl%6PPu5PZ0AFKIfQa3uzTxaen*G$$t~sNwk|(^bWLmz>UhtVor*8Yr}B&EbGj zoC?r3F3$7Wb;bKO+1EAB$}hJw+p-!*dN~5bDnp_keP^}k4f4n{y>|5~z}=!LO#Z*$ zIv`y0I~AF8A|RU78Jp|FNE;Yv)gjoa%kI`8JwZV zl8~OK6FKbSUQR5~ww*Q2AdigC_DXUGs39uicE(Wbv#y6-M9h3H9^EsmY2GDYQhw{U zaA3=Tp2*+p`ks&9u5w(lzVW#;gSo33o?Jl(xF|;V?}s|F^k)d#6qu!9W5fO}6BH7- z;y?p`FJ}Fo{_+K9fCm(AnYTvDuo6{{3o^slj`YuRepBoM&h#>8;5W3YpPR|Uo!u7)Oi~@uC8aCk3^x5JPry!{{ssK58qZk zFv$00?G+B%TFJJ_Zu$cuZihn0T*nN@G^O>22%899cif#?r^$g!r}MyMVdFj2Iz693 z78i9?xvtevv3VT*@p@+WY?p$LYI~K45nS^JOfZ zdAu5m;$?|&$SeE)?2dE4k8C?Kp5Dj3Bc)Lu?*JZEdpCXAb%rx!k?m;y!lsf9HBXtG zowF;6S9-K>``=4#t`%?VdpZo%n>@Hb!)TI7*3@LngqcP&ql8SArSfNf>zux5bR zV|d|g+WMem`mB7W?4PRkM@N48+2okOxZ1XJw(*OxQBf|hHtIjU3-eK(HQ!lVS2^aO z#Tc?QMxe8>$)GL2PLKjNuc>cJO}II-o>7$|7!7|PHui{+4R1+@NWbJFc|+U{!crp0)5@$T^3Cbg zvs2gc$49L*@Z1b2rWD)fqyV$=`9;z(PlSpl8~oJDKFOdQ4i)_uN>YD7bK9Zp&;fJG z$7@d*`K{#qqE$02JL$w{%d|TqGEgaXRMN}8nvc(R##RA!phY?yb~ml@)fVGPTt&Yz zGM3+%(>Ynz=K6e(-|PVW0r&qsbDf?(IqDzZY~G8q*S^mbb*E1O-XSS*OCWAUR3`wn z01L#z(?tDW`Q8%EmH8{N*C)%*t8SzuGPCJ~ij(8m9b`;FnI)JX_!SpdaLNGCP6i7p zezV$OM#n08ol60k$&w6iuSEKP4vaQ*=i^ z%Q4L{Ux|apZ2WEhkh60_CG#a-D&JZD3H~QXI+h=(`x-A(^=Aff>Sc3w^v(%A)7JNs za&@I!>|3-K3PEw`S3X|KzjdHLJ5=3_x|;t!N!rR|mnp0Tc)@JsFxxFE`m7Kj*|rUo zh160$%7ogOMfFRyx=9c_kMG#vk_R}>jQDj>idG?Y)ZET}86pYXM<9$3Y{6+*^USuK zk$+IfK1xoX8e;Oz-$97VzJ{bvV&rCLW1nK{XFD1^O2q1;927j zZ;rgk6 ztOrvW5OO~_Vajs-wv(Q@Fm@(YTlx?m<+G(#oADcS^D@CL&XCvvmh$&L^f6GLVy)szxs zF!tb<^`)Xz4~3PUOwLR;OkvJ{%8HY23~HCpp9f-q4GJZ91)Wty+GZE+gUVFuepdYu z;SM?F(op{Ku&laD6wN&?l*5{ILVe+9<951}C*0lG=MIy3;t~@#3CI1j;>Jr>(vp79 zi4lsf^mN1cAywJq|0#!}gXVV41a}{#D8Fr4Eo%3NJaPq15Sa-j+O4gM_S9nswg^hz z98rFj;g~y+#9|VCwu1LHgURQ!{O3-&UG%)d@aH2xa^6->R6-|ztLR^33IvOMj~~VY zKPez83C5l5zQDvUY5-FDE|Ipds(W~(FWgE;M&F4Ro>*9qRW9~*(@)QO+uHqigq!+Y zOdgc!Nj6-0j-FdCMl-gW?wEw#5*W@7ZSzHH%^cMYSw%BDP#@J51ygJ%soPr4Y$Uy%^ zctJpNAd@$s*t#t zOus&|<2Ba$xUXCB??29QB_VERX?LA@`E^or2U;p`{*%%IhJ1Lq5P!&4LQ1kC&c0`q zqW=%cFX)~mcNTHCco`6i*x1!2$J1CwJazePX>W~_=Yvxm3wd@sUy(TLkfF3*AL$gu zM`wbvzTa6PoSu|M&>V2RQnhitP*v7TKYV%@eS)W{`IA?r9iSdoPJbAs9OW(SjL8JE z|0qLT0UPPzBFWSS!Ij#xf2yrQP6Cw6*{-E`FT)o*MrVW-doj=li~P{N%AHX)vV*_1 z9?62`+XnH<^iU&HJr2$X1roVm+Eb^|J72Hr#aHTXF;UMmkMDx93)xKH$tUc4BCCSL zxgprtNe3FV+Ik(8lx&H;Co|yf7&Y!i{$HDu8>1PI8FrG$m6#tqHg%r4xtmH3R6g|y zVjB^(*WP{6aR#Nz!g9!_T&d`0UC zxbr`*cG(Z5l$ybgfm@PqNQZl>&HrI!NNqPhrK~1I5^!sU|iCBn2{^v%V5tz+i&L*Fc!~B(NC# z1*s!^!qn)$4{V#u?z@+6?p4eVhsP5CYBo zvza3B*>bPf7#K15`dXiFkXv36E4h+s(3x|I*GB84TOYA4UYIpQlZNF3B7}K;vT4^C ztmW=utoLNwty;@;+S_0ZdR)mQJ77tqNarw&H2F<1fd2 z{PU-#4<7x~td$LBqrlop2j#3l3-wjn=|B~71vh2QGcfO}7-SZ_s`&F?i09sue)9>)B%qT)%@1s4So>+-`HQinT{8U7kUc~ zBT7X89fSatHm(EyJ##T|2Q!xIsZRMkmW& zbrS`Du*@fq4y&?L;y)Z#&MRkT^LVODw$a@WM2)r@DhK4|J|-F+ZooJQRee@Z!vVy@ zHnx0VQj+?j^uq-Xe$k5^lh%Dt{Tqh>NSqGysMguJ@syBfV!rDk`7@@+kc#tZ-OrUD|qXCZHapWSy* z+rx8qv8y(`lOEFn)q3%Kyk_)>kL1SZiOcHnPQX?6Sy_s4NA&c1VA}?*i${7^voWFI zv9YmnQ{-E5qf?GF_mkVrCnuDRG>K_SA6me5z4GXQF7cN z^}l|BthZ_H>U;l?`iUJX7`7RwRMjX+G98JkA*kV)a0rPAT!LqT?6~SUkAKbGp_Kcx z!Ed3(7G?d6jDUSmO?I`hI(EI~Ievbu2-t{3$3xstSwu!ReYssQ1$F7gfa{Mlutt4~ zPcGTy*Jtt<>1!vRjlmgCC)2hM3zogSu6c{;?AwDc^Ai8b(69K6XhWspgIe*owq6E~ zfAe82K^Y>Y?2)@w@EIcKQ0(2TSdFeu64)KQ@2S zc1|&&faAyy2Me?r(_D_3^jUzox-A`H=O*4`c$U*WaK2aknh6 zrZJSrZgsJrp#0FLYMQlXFMav4E-dVzjsN>p^S<`}{*^stzim+xz-2l1dKR`->Mt#% zUcM~%>5~QisL*ts-cSQJ)KMbwqr%Q`x$(=tzw}cDs=)({{1QS^w3Bqb2b9o8n%9x$ z?aY5G1v)Er&E`j~IQv&um!W9c#~DGOYcAdL{BFy=YT__oj4_8>0f!aJ)=|0bu9t5KFpMBXWXStMvNvEZORKsS zUp@tOA3YxccU3-iu1silf?Mvja16^};I%RFbsS{roiUF9{f7I)>~_cSDF&=~gb+0* zrRO)2ZzK*s_78|>qcWcpjm0spTKc7Z%#c6)6TSgZu}`$;rx~9lVkn&B!{l0XpEIgdhGDwk%@gg zIIo-Y#40fiOyN%k=k48l(x&)>OmW z#vC-?gAt;fX2WWux$NonsxRg-URi*hhP7=d+#Mh6WF?vtZj-f>zb1vPOh)MZ|E4MZ zEv(jahkT5VJpYTNIwgAYTHi^Z%kR=i-LpC@nV5aV* z9BKvE-(QKsnU-)tIgSZCYAqX^d5D@C7lG}5eW+~jbzs^%w};+aC?Id07!nc!-3f|$Dj|w$_q9f-Y>ZI#2SYs- zrtmm`rT#K*{ypEfzTHQUwEtsx3<1Fr5098O{7~S5m{Z=q-2oMyeg{Ij;-O1iEA|gg zy?U&c=l0-9+OWQb^kI@;7|cN;)q<2~KA{D_Iy61M(qRRkX8Eyn5awetVcR%qZKtAA z0E7i&FFm*N!6F*w2LRHl7K$;*#R02#>v%;xrsvbuVvEz#GheB5V5j z1xocL>C^ACQEL-_-Mhtft3bP(yBOkRitkq8urjH&)*DKPR=mBY9;0Cv74#J6_Sbtn z-FGlMr3Gez)&6nQ`|PM#h4RKy^Fj!!OWt09^T3b-Ez+#J)Cr@i*B?fmn!|uG7~9C) zirKz22-IWQ)%*7Sr{SkR?%ut-x3_mc4icNIvVNrmWf_U~%tiZ$$-1&W2xZ(Mg?toS zez$X76|3gjz2;JdS8k^(`{(kA*$A(pVkT4>_2I)#OBn-ZA`qdM+)M!>W*AeIz4uyg zJJ+G^Q~Tpq@&s-r6}yZfO4(&ZY=7;nr=?rE8N=Pf=W7P+A?(***bVoS4C~3$`%Dpj z8#3>faMOrZr(x#*o8fqcHa4y*r&1YO4UKHG7kS5NY zegVtOUAs;oGYMB=M_gWb)IVB{(c#?;L$LETp6d?miXkoMfUwgK?o~>?+ zT$B?dEF=+DD$d3mAD^N61b=D2lJK#yFZdVM(-R4c$b5q4E%0GfyH?$=+<+oL`e7OB z1lTXfKTXHltU&p65fPan#ouN&HtD@R#~r4t)buFxG1p`u-A=`S0JZT|Q?llBU^Sx1 z#c|P1(U*Q=CHC&6y0)!e5?Wi!Br+W++AN9kF8l_e7Pi=r!pp zR|Fzs?6xUH?xvRpE}pX8V?T4V?iF8So=o4q7HGQvU2*F8(M$aqHkNwD`+WZas`P*| zyD`VRWXnHW5822#%`2R^^CXLeWJ?TOe`umD-y^d35+>kv1CubExUt`zfS@E5tJ)q= zGRWwiMkxUGu|aOH7v>>@d^);n!UOs>JXg0m#}@VE%PGtk=rL3{_)J4-b7r*(+sOD$ z2cJS+I2AXB3c!BabvL)oVD^A3_&Jq!u(E)gk@dcG%IK{$srAegDy@_M$fS&lpKW0v zQ1^==IKakp@Uo%^W(dJ`X;WRlAfTz{L=?x`4wg`XHjOWtS3!#RJzO3(1dm$dGdkE! zGziv{=jkwzP-jU7HEzdu!2YS)L1k#cIK`hcyMv9y-G+5WR_mx`TSEY&O# zOfEL4ki0K9l_>^sm|z#7Yfd>RtcnNxORSd`%G!0TZEv1%NYN`}uYCQf`Tc?2fLsJg$=7{p(^aDp{TG<@;X^f^lo_Ax_7#Bai(KCjeKqXU)l;(@S6oLiHY|d z4JMuNbO9-P-}TH(3w)_o<8|5&DF_A?Y%k^oNSdNs+G_;AvH)t|s?N=xCRQd%UTREl z)382o`9Bqy=4J{I4;S&wa)YGAwfyVW!Fs0J9_TQz$g&>P;b{29d|=z<_x@BwAEkze z%;YRq-g9PLtK06uRv7(ia#nEYhC}F|7-%Xg}^R4Jgx2QTRJIyDC;D68%CnMxjSi|U7Jjd@aZ$_Z_)s&a^^8#vidwI9DK!4KVWp0y+GZo*ZgS~k^!C#%L+YquKFsLw4R z1QsvZ5P2iZ4-3qM@!R z|8t(qQi)=6c})QV;m$CN5t^3>o${>*^`eVQ#F=4J#hzz(9Ht8d9-kz;0)Km;M=SQ- z1-BUxcqSrkT$LSXzTyBv`Ri*8CGoAt{8yrej7zksy4zW(Q&Z9nU!Xc|;;cbBJKhRc zymu{;2k&k=GyKh@8YP~qp#w3B?R358Dx>6~C8+Q%;aS>%-3=is-(R#@B3wmZBk~dk zHn3ZGn+Kbe1Q192GeXUb0b1^$M~=SYC4;3fN$(X2XyH(K@LZ1pQ1kU>)rIxBv@6s1 z7{mql-Qi+$-nCAbYOHO-P&Q-%Iku{hr``Ne3P8BF%e@MxTPO89*!W2^EDUj>p&fWc zA{x=$(EzU9`ns{sPn7!)k`_+4XTYUIV?icn`s{;Io4wY5uPvI+Ckj}z+C8b^Rk^=D-a zWMxN1HyumQ0En7Q|L`5+N)#T-8_A|fUf)z$s9O)dKwbUWpymZSqL#p+=mcBUjqQRYf3Tu=OyNw%8Mts9&&*1P_TRf_8c zjf;XrQpOi;AZ;1PB4ar`2n#7}t-sax=CqZ`Z&%dbpqSg(27^H)el6J?GOIPu-nBfE z_BSf5%-glA3gYMQr-{|usabm#3_fRf$n+y%pu`t>KBLSx%7E+cP{=c&V`Fk7uJLyZ8trSw|;8x4jEFSicBv2dH%V6E>)tir%Dp1Zz#zreN6tze}b!x6H1 z>z${2{QCAy^ofy^mAJ)UpFBU$$F~FH)+ECJ2~%MqS(diY8CH zC(w5$rKH4<9JZMf-pG%hyd!Mn`>Ouc8m;k`vmI{2iCqvcf1X@Dg zWVK^vfvn(iE!b2M5+b)WH{gmAM|z^Wd;NBN${@piwHY~-|t{X z+9zxOyK4;j55_Vw#!c`-ys4y=3-z26xfO=~FM{083dzCx2gN_ya5yub72Nw>c1=geO>QRP}KU|EWU3CUz!(Jc4)^6 z@@ltbRt=sGB%{?1JwSGiW%$Uy45c&mPcEZmuFC4fq}Au~r|i(0tZ?v}itlnfYN(G+ z2H0E@AeSB!)7al<-T-mToZ zTH4xUv8I3&+_LsLv;W?`0X`Co)}hjp)V+K5{Df#>IV^j{rf;2%998zp;Gs{4(A5?W z{Lfq5Mt}2jjn`O_cviK(Btr9z{Sdx+7IwnS%0f=`qLL~FPC2-nOk_A@c$z*x{^ZKH z4h~E6jTj4h0F^F?_y>3KX@;Ol-EU=iQ;MDC2nYdeb=9iRiJOHn+6~J|3@qlB5RIuy zvDvqCyyw-iIPO93Dt;E}`1|hlfa?BbrLxC5_?%H(#Saq8NK>lP?|2x09aA znwko4CU*Olv?K``EcE-8mUK~@FGj|}5`f#(Zup>}Hf+v%+=5zo@7_yNQe4&?lqS~Q zPDZucSg2?yrc_!N7sd}a{Dv`qg*P0Ex-_oE-_japRs}DMe}Sx(x=ZHrpvFnpvb!NJlkgB zZA&D3vQboVNol}kOx3)-jCK-ld)w^2v3#dUo;IP8gMqV=>ejn5G7pc|t=xKsAbSwV z{F&B|Z}x_L2@XEu^UOO3^*1OKl7R&s+ntG#>^c(I`x~^VEBK|_5J+EVhq6wjE`ll; zQg|J=x1KiHBUbGyJeO*Ju9=sGp=@BXa=adx16(V5ZQ{E`uC&JQFA5m`3g^EZP6_~F zF3VR19Zes-5j)l}X<(a>FYv1JCHW<3lJZwIk3$3*Y|`3Qh`00gE}+&66_$wc5u!sw zMtC`IHUX%*7@M7rGFahVOS(mmf?84|2xa}<1mDt6@J&R-5R?QB&$awbQ5Pt9o7TL}#X!Ooic6?GWb0If z=VmV22ZpgY$e8~ZU?$MGv+j0XfaX&s6SmOYqRp_Q4X0AIZA&qpp~9Vn@Lk+22oFG6 z%Kg?1yLoTv%EYoJ0RzIeC1^cYK98nDfq@Zh(l)x(*VcNAY(n-0>>T2cr3Tv`VrCTh z!GD;R!FrV8NahjNMvoLI!WOL5`d^9l+;m1U2S~@VwSrmIt>89S@E*Tvdj#0XmGCdJ z^Ue_IBa02^IJOv#3`Rvo$IlI{K`6ge+1ths5qI5bs_E}V%U8?c3ZfAY^(Vsu8ds^LA zN_0yt_t4Chj@eP?FGr@y(Xme!uX|vU)8&~gK~zEgnW~+*F|ctcoQ-;8|4BA+@T6yD zhtopu(0gt0>LX@$F3)V=%*|O($pq(`6>JOCmSMl{;;l(n?cho}EX2Fk!RK6r z)uRkd#W81tInFRlr(ol?Qn4M-adX4elto%!^+!*%S!F?%iq80Z#`3ii?lz^G$yu%=VhCaU7a!h@ZI~C_So`eG zw~Ag(bnnu-Tr-6e1PX7(5)ow@6{d$DmHnbV8W1u%{ILCyqIUI0npCX$|{iJSFR?((g zOx}yaFR|G>mSX*I)ZFWIj;zKe3YLZ%8X2_>fy^6&AAzw^RLzdqdj$+8IG#crSr@J& zjQKy#0=ro_^TPONmbk{YqaE9=M#k?R>FY|Ubc_X$*IgStpk*e|Seq1m)84RKD9FwG zi*M|QB_kSG3{Wl7;mg@Lgym(iZzH7WfT*FdWF5#G80?#~k+7V;`}yIqe|_~yW0-?{MuTCXI(ZapLy(883`jHLbR4U@t32)zKreR2*)(Bhdh z?VhX14_^@|Zsx7LFQ|{VxaJd2e&c`{W1w=?65u`*T$$FBpOTVJyjOautaPV~v(PTZ zI0o0Qp6?}wOTtl^85c=25RE+KRilk`9G|!i_+5k%!WWAqMA8nmsPfRruj<;@}#Nr(nHiM)F zL+OPQ)kxC!EQ$~N(OV@Q1&>H1^&RvVXTs6@IHgPPBH)JS1YF*bJk-on zqbs3cJ<&VXbp{?g>sum6Vj_LiR4Mv-O3FF{w9gikB}c}xrj0)wQe4<`BzW|1N1%Wg z@a!}Yj2UeF?avl;P|;fn&NXf<8wrW!{O;aQbLV#-A3u1)Mun?=JZXli&vrehEC%Sd zRh7N)0W$%^n1SDcE5+#GXl$>C@oHC7=7C!{9jPM4@T*06t>!2B{Cc7Zd<6l|!`5Y@ z4B>XH6vC$WuJ9vh$w}-&E0(H8xKNr$Rp@U@VRFoYr#!LpaR(jvcv08YH1m(W889&i z@vJOBPUSaYbtpe%0ps$l$BHc(;o(lwrdNWPV<-eWs&MMTA*x;(|8pCElr88lciDWg z9xN%m7E?v2HPiN&^gqoXh${wRdRat3F!|iouCG)>#d5E2 zE2s$jw-BDf#A&%`+0IRoJa!Nnj9HbFB6&`ol%UdLRunKJ&qzDZN-fj&Ik(9HX8KB5 z8)+-R0t6ol>J4}KTe1Ocj0^zdzTDys5W{`vcD=h>?;IFK4MS7j-kcfapp!cl68ws~ zAT@ZH-n@Uuiq4Pu9ct!t!thp-qQBMjhGw75Ol*Im>c&F$!@=I*7WAmM1mj3Mwg7mw&f^KLgqeppThUz_KoAP(((Y|&p`m?>;zn$-*p>-!L9AUFeJ;w@jn z4D;|9*294Mm>f)%&R*)p6zY8XiekrVG>0B}_IgnmdAWJpt(n@ej@o83eG)r%xO``j5_h zY~26=nR8Nyc-gK-IF+Uz=4E?E%lj_mPegb}dy`Q1MWjBgVqPf(tftF#4Pkzm^)hm92q$^(mqoF)~>9ds+x5hqWb zsz8H7{7;n8Z%x-)61430reIQ89H_H5R$AsfD1S>KN=%S*QhLu%LKSAsQ}nUw9x>bc zxZebsfx!G%F)Pyl^9YiS*skV*tBs7R2OPGJOP}Djt2AcJ`_rJN!i^&*B6 z9@Uk}?6mjU@PX?uV>t3D8%*VVO7xp}u(J%^w)HtaW;|4;7zJQ4ux{bh%{k`1WOG^H7U}6zg;) zUWWl_@z!reUpEGY>Y!>0)8J8?hm;ecU5W#<V?HMH{69FhC#gqigZ77iR$K0!100V0qX_9PL-0VodID5 zV4)O$nE4T@4blb((lKNTiDT*^ z?sk}HJ7pI=cDQnBq8aoV%4%@HY_PaMJWJb--fHQRSKNrWA)W0eXDvmLgi1IMx}=2* zO=6yCLR*+bu>H{JU$Z0SYkx@4OVM?mYW#zWPU-83w{XQLZuQdj6 zCW4ouxQ|a!XII{P-vPDY_AyxfT}`9|o&4`i{VDIH#?%CZ&-`|8WT`7_=3RwCW4JKW z1WjzgVIyUBkPaz}KE^^hl2*Nc5~d?yQ-1)`w5S>kY%%NCw|PR+c;g z%m}dH)s^C)xPCm`p9Xq$^;4Ce(cVrBBzE`~h;-AF=6kbwWUb9jbjK9FzTtaRXBV9^ zzo4OrZz%ZBLqnlObq2dibq@UZFY<0tSCi4StX!3JRh5sTJA$DQ?4%lpKw2}D9%ohs z^h~3XfgnN)znz{Rm@Tq*F7_*B4);Dt@iBr~VWZYQZzm@r0LS0VO*{ISA@o8=jCRrj zEkh0!{~Z%hUPpm!qpT@Ln{Z=Lhb9AE%uEOV*rwJ%dSa zZ~C)X#oYa7UD|e>4gcLGeCF*wtopj$E#mfg0|m-HJ|GL^LoNg=UV|BEQ7fXae~Gd) zxB0J-A_?QBL+}HLb9(54*qaq55Ue7eIjozxX*c)2hc1O6R)K@+2R^Av&4)za; zJeO4|ka$S)oAXJ$JH5cMCUZ#-)p zWM1&%L`Wo8^Bu4LH-=JYng6Ad=R}suK^ptgXME;mXh~`Rv4r5@;G`rk#z1nM3?jNq z-raN>CvOl)``=;qG8O!m+5CQooq*vkw80!_ee%{JWka_@n3M7HmcMSNlih zbJky#1&8gj$Ek)_S=_{WK!e8PW!tcvl(E|Ck&@C%*K+ezZOK4??+)Y+{Qo!h>nB~6 zT{GM6BHn4b6~>aZAJr5q`4b#$?8{>ZC-sh_&90ocWo|1!pZTXkggz`-nrq z_`yl*jGPEzn!| zSwp%Eu((;8T{N=r%o5k%c@eVqZg6kq~G1+L0-GbT{^ zBL1%>+#=b;*(3B{eLCt_PWiFB*v8_YTBF@kv|qMIm3@BtRgAQ%t^0eFC7^X$_Fn`4 z7QJe`=2sTCaB{^aRsN7u!d+nA+Yn}WqK}n&CVCC|j#H;*RHNHcy#-AC|H(kBzW)Y`-B0es$K>PqoGVDso4>MPECL#z8E1bspeX2A0H}+JkMwX;PK@6< zA)>}D=PeL&c&o2Sv!G9xXvhQ^nDF zpX@DKKVJF?nx?C=Qe7raB|{6S0#fVJ_?nPbbhqPOrBJ?XilE_`Jgbgp$ z1$^D|N5Jkbpi1#IvHQB28Vny&<)p2Ar&aVT6%QQaz`_5q zl42iz2w{HUo~aO~K8k&Z{#Dio(a)d;pcF2QvOd5bzjC8^O_*>N>cY+skQrFu;BYBy zqo4NSH2QjYML!z@`&KD7lcy}y1FiHMW9#3kDz9G_W-mvO*`{KT=HbS+k8fT;uZxrk zRuua5h$Gb0mm-10yr(gV?LWZpVKy6lHVso4B`*m}JUGOIv4)qP+0xEHD&W=ELlYK zlarOjh5=D!j6_bhjxwu4N=@K>^`JmQXzb^)x$Kx89n_kDQCNR8nV<&yk846{dE2F6 zbK7&fi6)I8=rx5<5BQ9iUr9@I_AXqv+7W#k2DkQELEbqXY|zOayH8NNm+2TV7Cd^? z;cRUk?x%>_t6x)c%p~0jwTdPWz^2Sn{AY=<2xd6lV!zejY`AFr?V1giLHPZAu?Dg` zSqEhT7)a9=!<51zy|68VGI~tRM}S0uxt}Q$lE$ukD#FA1ft-)KE?jHxnx}gF*;=~) z{SaSDu?v*Z@^+QN8(HS`cCdbwGsuD!!E$3+)iaJU8%r}@WZk-l%c9|~F@;sMd>fnu zMt)v$pg!p>A=U&&RF%}Xc9UJNDyptTA@{l+4V$j*ZPTg&P;+<)mZykx%pJz<7`*}YK z^-6|R(C{S5O2jY6mJwJfOL{f-nU66*%8jjgSRu$l@{QIO8o1o5>Qm>YNt}XU7I8OT zTDbx7kx#Qb#5$%Z{qIM%K9yHQmZM+71FZp0hf{!gBPyeaYC@(bFk+Do6bCY$qN1Y9 zd%ddq;F+j9(!0ZEdFBhHf5172p9aRPx;2E-SCd=R|C;q~sCIxnMUmQ<@Qy3{HuUg& zz1j;KAPY*HtTB*!)M`aw!6<(}uKdICPpFSZTC>i;x+@bC#E`k(Y=3eE195`wzMut$ zq8~j378dohakD?ZvYn9gDr+PsPHm?H(&D;qTS246R_FsCD2>2#mM!cyO$GM=eq?tf z>al=urNa?`5zmgnIzf_8#|$SGv<7UvzaFesKDD&Z@sF3_%WVz?KC}l}d}Tkv*=?2v zBkXPcKB}rGqa$h=5q5w;eis9B4Ce93VK5M}me)BYi5l29Iy#Pxs>=OKj*-D+W)-d1 zl%f%h*-_g9$8-}=%tT)cD<>># zXCz2aq3RaqwPIeUQHm_>B}a=N=3rjl&G=G(r>NzGlxN|Giyw&~F=rld4vsc)sJ#6q z@Kqsd{FBe`(D61=EH5r$Ce=71$z6iEa3=~zjoPT}#%SkkB`5|Tzv$LCr{sqG{lHUf z_`t#=50*|mBa-*l4fS_>FSlNf#B(P6(xQKU!4d{++5jWU7<_XcX~SRGC#j{ib&@t~ zn+cx_sMEPAjFW`>-hC==0BqdGI0J?|#_g+J|9UnaWg;uTeeYX(%#PB2N3Q9q3fD~e z_G5>5jWI+vH2g3nhw@oJRR>R5!WX9+e+fzuMf(LNWqo65*u|i-4P!Bpl%?LRr0lnX z5dnh@(*@_&+gxmf*3JEZ2)#JtRr#g8CtG7MPp*OK^OZOuhZGC~@WD}sgJMAY`Vifp z4T&Gd2jjC(a<)Bctynlq9(3X**Ugu2Qzb9Pj?DW0sh@tJwz@-yoIQ(=g_P!1m))U2 zl9dQvMVKexSufwjY3mF$5HJ9AH|G9jB;gCBg}DS(0M3TEgUpV`k=A$a@gj8PkGVVw zL=u5(!e>jm1+#*gb6XvZPGI0dsY=gQ*NA+BSiWnSz=OIGn8xx{(KsbtTkMM^T7WHU zUm)q9*1#dmmy>;n;u)sRo`pL<%6VJZqD3S;6n?m|k>VF$LGmR?xb8=qA*0& z(L+bJmH**qjL(v^kUelZ*xWc0QO5T0T1{s86q6#2M)Q)-XL6^~{v`JbM#cjgE4 zyaco74C629&==(IHzTKuSIA;-@!6 z;S{5BW(-3w6rgDQaW^c|p{vGbBSKfb`pfxwokP6cQ*dmD9HE!Qk2i88sBuK1e#8Iz zruFer%qQ1PS5(QMAHFhBzDlmz0d`6#;@8!6A8ka42a`zh7@|CSV>nGtK|%n}TG=%K z$?z0_pkKe=y3R9u;PcKoO31hPSUK@;18nU6d*$dZbzLKa_22GF7#P{k{xko>{Hn9b z%*;po$N2#&(#Ge!j3$p3^gF)th^e)wo$$2dx*TZ69)|!{B;x9v?f9(?FMYqok1dPT zfB)3R3~|T8KAk5(5Esln1J0p+UrrYIU`Q4g>A+D-snGi(>T`ZYyPGx^h=A&;(lgh4 zT;Tf8vV^|@cVU~?>DQH^Nk(}t=K3s6n64GI#^)lR-C_z{HvWPpK?vG9NHnfp^Vf(9 zLziS&j_bs~$)RpYuF5xIdZkhe#d654oGIwq{)RBwE^Y_z=$e_~XEB#m#!j}%+jE3P z*mYhJV5Dgh{q&*RE=M|G^V6_>=KSC9vem%hO30`4k1Sh(R3XJz^+6J!cPoVBK3*OA z_D!m&vG({O3pm}rFwduBRaVprhlqfIP<306!fVTS?Fj!#xrdYR`|%GneqUy9oE7T0 z;>n;6oPAe2BB!7jWmbjj2@xS+)=5;A%dPe5hfllpSgAdt@qP{4y1K{dxQ%@}ocZT4swFKYRYhKHtWe zS%V6}vR+FR{QjziMIm0tt@ z`+^}0bte8eq6WlV7JR#Iy^D2x0bQX9kGJ0JlOwPFYpa&}e@3Qp&v;z&^zH9LbhN>b z=wCF{xfEjv{eXUq4gs+EW8!gj

~dcLGd!-RO`q`OZK2v$1b#iS?kj;%i$=-GoJ| zo<48oPXm=5HH%-{G=17Xh+J8vV>1@37#AWa{hs$%p7YVdYs1;%WGpM>q};nlrHZQ`{#+>FB*V?1;r;^>K$jBe3SLD<7IX|?_hCA?7-B@{ze02mg|LFbzIOs5S zTBOVKv+D?82zjctf`M{1W(w3d-s27_R@sc?So3;H_0}`y>5@KK2?chwd=~Gh9a*y^ z+ny6MgNsM<`lfBK^|1{E7SZH=GQ9L`lI6a1oNKsQNE=p8n_stKI738ncuDwqHcVwra|cf zU-22o7qi2wVznA$G;yMMxqw^n?;ZLXvZL zaPE8SzIE??hglAbw3BoGf0u9X{e594-fFkyWdyuDZ3SV$5lA;;CD0v@8cHoArv427dcaA*2NGcm=Hh4-? zCJ8A-!>R3$d?wTVyRLS3&t7$o(>H!j+=YWxKEvTf7fj1zrq>)_@Cw~F8ntMmloB*rIe zDgxc?JNv+iHUl~&+OwpX{)>l*dO^R<(y50ql6E=u3Tq!uJ1OEP^W91aSevyq)J32{ z>*!}F;RNHr0?Rp74tN%?BEg=G(J5BPpxAQB|Is3!SDuq^oK>sod0siN1hh#0X^d01 z)Pdc8>I}$j0#=Z5ZzOj=6cYhZKeD4+=hioXIl+Ue49;w$-s=kel2F#&zuvFaouaq# zaSy31e186*Yu1HZvI;-#rXLhD@xRiqJcJcDp?CkH)9%TSk5?#0)fmKcy=F&zUnKpN z{psS{0CN6Vbq=#3v~=-cZ}gjT zyx*4!LxAWhqq!OHRJFZOY)jY~c&H<8xIg3ot6FimGr!pEty>0ZRf3#`cTb64Y5dQ> zWtuQq;o|El8)yBx-2^z@bN4JtD`qlNa|)09GQ%B1*TzAdw!~)Fb?WCS`0clCtXyY2 z_65H3zgwUJZP4|-?WqFbX0DwJdCkQqN_pU|Kx_jVb5}_Rr+&363dRN3H~prZKEJhB zuStC1QMsC0w;s^NLByWG!fghC`yoWmON|J{deuu~&yXumVtrN}gVIl2rrTeuXv3$( zaj>#`189@?Y+YzsCpdKj*o$GjNHK zE0_&|HH%gxiOZ}hBykdp#E+efoKPH+v(_!!X){{i;PfheW3sW6 zH5PNf?HfTS=6!9Ha)Bpo&S*Ish&ay$PQXwQl)GJRg6LO0I{cevce|wYB#HM)F?36A zp>lkgwi|{nh?CKbE0#r^#$N`o2Q8AlZflKadjwlJQE#jjDDSi=;{@orK9(-)>+x;z zR%5Vtu1@~^vcIjcC8Vn~P$iJL-eZ8*d2MyJ~^|Mgz~FZD*yHHc^i zes7;wsHOh5Gq3)F4y-Mu(4*k`t72gK%F%Oa6S(Bv;QlRG-iuP_omW6S?l1fQS6Q6o zL*M|}1K$W@UiE!ND^+w(L)U(|;=QhvfYIY?bGXi$SxPlw8PntP1w+Aw-Cq0xl82Rd z^{KGIkG4mt)3lw&Q|KOSg@UJ-34yWpLH97JmwydKGRrs5dVBx#-QF3&{GH66A@!Tl zeaUsU=5}_|sf!aQOphiTh1%Q9VmYqIQOb@Szwu&-fJz_z&Hc#foK?^n8)Wzoj0WcP zJ(O+40V-Dd&h}XAC;nZViNS%!yflv#=pjq0kt`P4wjH>BHH}Fo@h*6C&Pb9+$DP`4 z&{r_S^=xxHE-EFQOHj&DXZH4VFD}PxHul~e8seDH*=?1^X*Sai+Q~*egCJi(l(Z5O^gKQ$Zagu2&Iw3J;0tknHo&OdJOoYwVwd&G7C925IU9| z&zbtSP2{UY>BOluew#~ySpn+9V_(!nf(tdkcb8=zPa3^*vYNJf#d2^~%}G#haY|3IkG&3rXyW!yn<-80NEI%cqb(xn>PQZCct01yubTZo+IogqPaT<{2nL z7|r#(&O75dz+dNNTsYTXsha#?!AhKd=ezuRm7{*+{)M^y__qN-Za6f-ucu4UvU2@~ zJUNv&&+tRWoE_OEThd{tYKyy77Vj}(tHc}|_{;qhx5RDxy{a;`sG4^sMTc^Veh?~C^tq{@xkkLL zP=LVQd3Q41=kL|F2cw5&;%I?%(x~mghmM!eN|lI?;lt!uT_B88ia8hP=8E^JHvL>6 zm>4!GTRY}{kMk!^w}QSc^7iX-m?T8$JedtwE}MR57RgXz*1I-|dY1w3tAuJ3S>oK- zmWP{UGGzxnW`6kMAk<$Gt~syG67k-&I0KT^(4@X+lX}+^7+`;YVmoe?3o1VHTXZjwIL;5P>n5HGhRt@+QBskEqets1mBf2e;7}k4^Hnx{kHz*L$UK>8VP){Dx8`C#oPQ)I(UKY3 zOEyeI(0-M06Sv>%dJ}F)i_^zXp8B=xc7Y_fqwB{uqFjKq_tzCfOq0NZ97*39d7B@Q z`^8m_!@TWwaqcJ-Q=kRvw*e}>id``H7CQP!kbpWb1sxnfIT@~XFKwTuyJUDX3_-Qu zb2^dmUI2RIpRJXlfH9^F0o9#MD1*wXgNop2t^q>x!{jxlR@#k-F-RA#L z7$@$6$}OZ&>rsHD!U&{^obgCOw_7{j>DV3sj(!)%ORbM6)S2Dm=$ntRc(+QeE$Mrb zE-26B>K@@8osx^n{90L84LE;AAo^HyWDon`barpibF^Uh$h2)%8uspgKqQUG8XtQVY&%C$1#79F4Bd4wJ4i z^jzpF-HG!~&s6zo7mR#RWYNgUn!!aB#ft0jn<9&coR~6%1p5Fh>31;e-|7;IPG5Lh zokFSExi+y<2Z2osg z7Vnw>3QJ(d_Y@r=>eX3}w#WqoLpYY+HiVj11p8nl4W4@%_b!RFMc&y&Q|i{9@bhF_ z`cfkys6?RukX1)F(b#6cqNi|$bNdvI#~coqnzqqM?wu&f-TU>Mlh1hh}m zl=U{vpV*Z*_D`CJZ~KpJX}T5u32s^Vz+IyydB&cthdpnmn`Ijk|7m!Li@jql9-hXS z%4Z9VI%WLk8A`EZK^9|`ZCe-9%hEz3zx4S0WSg|Gh4IYv>H6&4h%NUxv&`kf=e`Al zpU|hl(nE@Ap6pG}76W}|l~-7%<4I1eW5-u4_(v38B15OYL?ys04@t*z&Jp|X&#qjW z`)mqn4XHUpp+3sTF62EXJ}edLpoT@%tJIS_sX$(Hm*L-?zg^m<5vV?) z04H^@`7jzfFQE($%#u+20&$2W6PPGjV&8{BY*Bdpe9I{xZ7Lx5Ktm1#bb|Txz)}aR zYfe)&3C{G$%&H0o;BHVoG)OhXUQ?Rd*331Yk_h@dV?(K* zw-}ZbYja&Wsj+?{7&Y7)JfF~1&QIQ^kD1?7;2CFh6ST;j#WRH|7LV($}|$Ephi&zWI{EjeDpFkp~c- zi9p5+X+QaRN)uz_nh;onJQj3B0AXk}3+nPoBQQS~6t0&vROcZ6Wk3!88wsPmL`{1q zDfjewT(*R{T0n0f0R`Xo4G;E7NC4yLiY3-@JruOP)!Cg$fX*<{jPoa`1%mQm)aG|z z7Tp4YS`s@iEZO~suRaQU6$N>1i}dwuD;<0tfgeLCf6jSmN(*jLx|H4STS))3C-9D& z9lawiP?z{M(af=}yF?e0u10ny>ULSBw(0V%d(#|!)0_>vFEQ?6N&%OsI2hS6gBR*H zhkiW@nO$fJZ{AihQ9F}re=e3;IO~hiz8)F7Cb6u0rp;sdboj*;9>vW%f4E%dyXQIb zcGvYulnoT}0Y6=H_7@emZ=WADYtwEob}MZteSI;kJ34Y*DfrnyT_+1m|K+IZ+c2tS zUkFiP?8K)F_pQ0d8IOB}I|>C7L~WpVO)Pv+86tl_UqWka3t{fr@zm-4OGKSpJbZ}F zZXOptRJUKUgK5bwABKLt@`PXWS7fQ<#@8p-w^kVhOzx<{7vDXM0DT>^%!> zmu@^QrL&TIF6>BaFP=laSqH$=+4RMKt9}{G)K>xobwbiJw;>XDq}y{eu|~#YZhy>8 z_q?BEc^V_O>Ojo(7rvoV{r`AteEKTg(+K@IfBZZ*ANI}ID$Z#5ls%UDcd%cyMxMf9 zy=I8)4N4c+cKiJ72>tqVKCfgkt0OwDFoQw+yjef-^`Ec9H9no9y&N;5XdJS7x*pFy zw8FHNQMGa55>}S@;_2bmyaEDmT?1XBSVGyj(V@nCoVB5Jiz|mPG)}mG?HA;I!JXTK zIdmwiy!H6lj+*FWZyGEG1HN0pqV?e1qoc1{cVCjDPeW+RgF8WnJ5dSEEXskQXxDY> zv}>OK+42Hy)|Eq|F{$$@+oY1As@5~992+JH05uz^KuJwZ4)pdW6G7Y%dI(g$rg^lE z=AU#5GvQ1tX46b|o&N0*S5FlW$j8r}GD-R9(r}$uTlcGFr>J28l(3(?h2|S5Af3nO zVtwvIl&QE|q>`<{0#hTZe9hdL`Z9il%PuYa$8eRS$AUPAz&cs^4o=mv!qZ-L z6@Omn%`b59Oik4kv7fY#KCO&&Zmm2gKQf1%vGTbpjw+=NG5O>Juo3R{uy5FMn`EZq z%TiTB*!l&oS*Lc4+iMEDbM)|Cz4If!`Bm8W6-iz^?ZYcPDo?m3$WKqDv3&JzJ!wyu zg;~aJTf2nof=yroSGXs_S2tLK8r-60y@IHtp%(X)nox`!omnt$-x1_Nd+^874Py_m zt)qA00P`zRxk#@BP_pA&&&gyX72el}mhtOMex3e}aZnE|?A%jHDgS?5uQm(DQeOiW zm(nugqpcl&Q#G!-4}j>UYCGgYV9kIz_|LmA89E_pc)w9j&v6wW;d_e99r%GMFpVCy!CAsu%7SPhc`k2ZXa%`{K8$9b5HZ% z6**${+3m&U5zsc1kYb!|V z9dTug2T_EslD)6&cJ%cooyd^u=*i3S2!E_SyJHm%ya{ebm$3LwUey|=FKkOXsIy`j zO=Tyy-_x{DVBa#$Vf;fqQC6285;6LZhmL<*PT@4i$@-d@*jFv4;h{fb`g23U~CzW++Wi^9$xyzerrn` z8PFx4+>3c<=4#lPE}PK!`7KA@V~g!o-{ZObyW}bXSX6HhGe?r$Q-{_p zMxXQR&~0ni=+9J1l)X;gH*nVV6IV{tRpNC+;$KN)UlpYnDqQ_izpvE7yqmQ&RL9;x zUAU5Ftr&>&b0k`NerXYGa8eb)ni(TXJx&&hK3Uo$f3K{{y7~IcA{hP}y}&H(Z+SB7 z!*s3+!YpR~v2Ic>EzFV28=1jI(j!r~x$7;ca%inT^~PTE{6@xi^p;*rpjud_ESWWs)&wp zT$}_%sUuXf;(CeZ%=m>BrarjP>_yxqUA#nBx~xLRB*~5mjZul^V7ZzklG;>5ztQ@L zyCNAf%kRJ}6i{?aO3!%8szXkxuOIsL?|-+&LwOE7dy+{BwGp}yD?x{$Jy1Vw7&>Gy z{-$;GM@LjY0!O-&E>OwM>+s~H*1{Z3aeZjSuFosM?m-6}%*O5b zJsSt@j1V7nikjGd_e}A&;}VR|(D9mgEkE>_&Xfi}mgd*u=z+|)RFr_C)X_C_Nj12u! zP#D!_Ials#DCzHDLg^g}Wv=5OH=(}IFy|tzeYpL1n&87nMKcK zwovq;gP0yLWCWoDXZfGzEr^hpQp z#^%&nD6ENkFHaSJNRBD$+ZGQ2bCL;E6m8y`W=EKC9_#v(H2{cS4#rh>wWm;nkO=k| zhCON;I|gO%Ha0fmP{6Kx;n!nXb?CK$_B@O2ziF+3pxx>&j0)dUk5{4aFN0i0O>Mv~ zT!Q*moW1ccz!V|IT}M%}e>^7<%;!`Z*y~%D?stQXdX+6&DtVCY)>$P3em^h==gnLP zyavCMwlg=GtgdByB?wWX3dDY|t?N+Vwev3bWPHgOk$%+;X%%!0tqB3Gco4x+EL{WR z&^H6C#5;dWP>A=oRdCczq8b-kxa4d_^?wXY|NT$Qa1^uvQWymRw%eh{3nurpcm6B8 zvJ>~}FM}*|O$`n39Y77TDj6p2dnTbP`tM^S?11>!(B8d2ie0qE_FNq>KQMt6^bS-Q z3gfnu$%awy=Y$!wOS5#xb*pIof|kOBR?w7WyBE^>>dBfAyc0Kj?!f;q2;r zYXe37<13?-vdF~dE5LPW@CzCcq4R7<7Ia8KrbM>&!W~!+Dg|vmLH!ZKE+B@f_O0GX zZy=}R9@_4+Lu&K0@)YjRrSTM40)RxWXaeB(Xvv8LU=M_Q z1U-%|&X9)vG!5#&M8}CcZUIXZ`vkFTXIn zgLAAC0&^BinB;jfhkT75m^DPZeU~9^e7DPDrsT3&TeEpvb3sdv*VgY=;~Rn-={3sk z1sb1vCg-6iEQ@OU7j1h?>xbTwiBmjlTA9jT+I`}NVC7zTmb;tKD%jT0<<6|~xX|_7 z!_ckArT0pY+Q_`c2DNRY)HvW);nr>Yr8{A@tEXXSyn+ReKW0Ixt*@`ItBcah|MuAaAaqjtU&Dzb=pE{+lC7WDf3S&vv=CR9AtpMd^yJ`?JWA> z`)4xhusE(}(&2!0k~Qeqn%kA$7agniep4~tV7Vseo#-%d-&fnv&;TZ|wwA}vAxsVz z^fqMGE%W%+x^WdwtOzL8PP$&N>77hfmJ!!|=fM&>;NmMAXy&R-8FMyGuagOR)vT=t zE6*JAJBxipcTh#GK(*@JuV(RvyS@nQVr5z8+1EbZ8pg@cig?Hh?1=STyU@R*1pvSZbfK(KYii!$Qp7Qti z=Pn)H-QBrsY&B(BN9uL{yc;ULaDN8B2FE$F0tIK>tc!hwesuLzK_fcFV~B_DxysF;(%f+AweO8^#+}lnQ5fmeN;gKre18Qr=YVFCRKs$={(k} zCm77?eLuKuULj?Td8s#OHnCr2f1m#HWLiMVtEBowdC^$tkf)(xSoi*CmIXkxo{^CO zUs;0%RHZ4)ZO@&1VDd_|gI#|KW@0Hm-q$;+>q_6dd*sv{_;O|yteaK1M~!wX;EKcf zuVaaRwjTTp}??%Eao;>8P?>3s2``j!TW&_vQ; za?@%BYBbjs8qM6@W88I<49$3R_41$VlmA?Sf>DX(;r_e2xN6Xq4yVbF4)l8?zu()X zb{*>aT41QHsi}GV_%U!L0U7T5?Cef?^{}PG@Z!Zc)6b)#qG0s54kK)(QX3j>TUsoq zeShp>dJXqtnb@{pqMrlWVe9|rY5#YZ2VK-3OW`}(+KT8? zf!_V_c*{<4@!06-Xc)p`mDJW=l)_tFzpkL>bQD^dT3yq0aX(+lsd-$$lI9mj+v8V> zh7&^em$zA}PfCS#*LIMH#;ez%ioWV2V>{zdJFZnVEzH+)*uX3;EyW1oO?@!>;%HK37O#(WMz4t`laMb4N)#zTCE3iF!PuVUV?(W&5tK6fC z=IeBo(IqpJUI0VECRv> z?%Pt_^OBOD!J2I9YMj(~pYcoV-(Y_{O7tn@qmz>zaUMAh!GX8+{x9EXEAR7{p@Hkl zGL9vgC8N1V=WRG)Oy!x$n^c{SITte+I-TwdH54)kLjB$U*3{L3*#z?dn*g*9!C+*k zr$^3ZFA2;Rl$DhMoh$GNzYl@U0<(@6Mh|^abE<1!%~Egry@}OBSU!?u>k7xMw$MQK zl-%Y@@b=DoN>+n=D6j5##9ivXoSAcN@EOS9S~u*~@tXjS18Q$fL%ha7_HyZcJLCVp z_3gUYKTol9$MA2XXvrr>Cof=beY@MNvqO%oHZ66(d0F!tTJ378K3VPLcg{N9-uSsj zoha)y_i3K~3@P#0oVlsHT{eqfx1Dxw9t~NNFw_E#-7ed}Nno0Juyh<=I#QR8Tmzu~ z!aB?tN!^DYD(D6V3!Ca{;{F+ERXVlu=OTh^{?MQ5V(O89f5U&PzZFFG#JTnq6c!q8 z{P*L>OQHQWiao$BrMf~zOUu$~yQ)GVEXGTsV8^L($mVe@ZY=p=BngIf|8gOn9~^*&57zP zZPlZg%D$SjJ;iH)f6vC)INYel#?sP%aeng2lMOa2K@{LGs{?A5DN?j>Du`RebY_L#=j6}ieCSlaM+H?rnTB#YjSuj*C}dux>ZetB{|Nj^LT zrfUZW9V}PofXlww@wSR;ZOrjli;b4Ts2wznopdcgs=Zl#;NlGk;CWuf}%Dr^GS z*uW3)1`9hCIze-b!!z%vx&DgN>^JmC_NSB?<@$DQf;U^X?C&qDaRF)(!~Xkz)tGrr z#!C3tmjuGMHM@K{GBR>dZ-3E&5%9mN7qFIat^NIB-`}`#1B#A{4+{f1Q%=eLH-RIf zE++V-Q=_7I>;;Fve><_I0O-K~>%&fqg}g0A`+w#8PffYk#V8BPWrQCt`wJ3rUe**Ufj1s_pc3+kkBK)3wN>@4taoCM)n zO?psU!|m?YRGCH+8e99KChCas{!OCY_n)f;Ck&hNw=)Z1sdII^EY+M8ElaD*ska}5 zE<(2>Tyv@I+YgpAfkq_k$&-s0FFGtQg-?Sq%k?aWulwRo=Pk(;n(Y1r%9zjP)!C%& zziKc^*9@6mnpK0pZ$SJq)9fkj{Y|B&`cu6w9v-oCCQ+!pujuXVhs>N$sH?ZPw^#ow zgs9DM;~9JSHc!IB4wu74(}7hEEJqM;iL2NSPL)~Pm4Z%&QStGA;8!QU(GD zIfce*Rta1+;GlqiA-qmcKMD%H@U517LFdi98kSB`hV9Lo){j`#2X9yT4xdE%|4sX9 z`rGB5{{_A}AcuAt_?4FUmGs;_6#Q@HA#>SePnZ+0YkoLtliIR|}Q0AE*c2B)Wp5)}u7#8lr#8~!C55eDkQf`46OVYE2 zP#ieA%!9}ugtHR79IGO`j@sy@SkbN`JsNWX;X3x^cvye(8j5Y&N3h7UD$2FzbX3~& zLDm`(&U5Zy6SRHD+Dy^91R)>DNPtvK0gc9(Q$&fDva+(0QWgYmt62^k8h~CA7NPzc zng*;?AX@fGlk$Q4{Z1$8bX#OkcbsSh@b71KmOsqX>N`C-P&|Ze3ZC#I$a0Meggn4@ z;*LD;`N!e0jf+F|I|L+UTpk%bj3tpsK%xT2%a@;l(+e2M_+11+7w8tiri00?gR+j? z%QttvT5ePo@5e%&)e+h1O||CSi5b;JqNM60L9)*1rO z_1ySlYVb7ndRg!7xP+dWAu$G{=hC%uHCY$$wpA^UMq;p114^2aML8$He=w^j14fur zw3gLxxBYt{&&jwP&Do`pndE$4yazNUY}^(Ghy;$%eM+jxMBiXwYWiySz*OISYHtIp zvcVu(k$1Fvdy&7-;ALs|coUaLLuKC@sO82&{4V#t9AN~TJ}r-XNous<9gW+;=FO7j z2xXWBbgxhvmU;1T_Sb~a^FA2&Y1V@3>%2td2PdLP-qND=Iqw7hcS@vT|^j(05Q(MGRz8 zpcV;Xi0?gX7X^d6av7twD@3t#Yw5E!`jF3ULSmPR^6n2GK0qPCUgU;=Cc*v3hrSLA z4Dg^VfKX&;GDM$?(H?0>;I#zUPD3vpRc1e= z8fgl>28Rz=@+pXb>AKl~3nHy)xkD}bGQKFG4FA{KnM}#(&ZqVQXJc(?%FZYoBSiFi z9XdCnJ#9i*ElWJfL&hn>3{=Nefx$=7x8_wYy_Wzr|HD3a8nmU+N>vD4fDtn*L$?ERd1`i}M0@uz(ec-ia)Ig(p~n(9c&m zp7Rx3Hk)KJu8%o4u5Wsi+shpD;8NRW(=zCqT0#QCAr!U~c?cvYY1afJ`sm_uhN}>z z2j4t~cf9G=?1JL}ma=Hmh8Z8jAZ!dXfRD`*T|jGv`Nwj}&vIz8M3kL0(uAK^o2{D~ z$-!*Za*`(SJ?nJF<`!i92e;`8Wsrrcw`atlc_+?f90Ah9S6pv7ePN1>z<@A6R5f3P z7Jhuu?rVv)!nL`w_0#R6v* zxr0%rc!Yrtf|EhdL^111L<<<+Czj1BuOirSg9r<4GGPdjAbeSYpb7ISa8fP$_!$8S z>G$-+&wZ4sogV)>GL4Klp`Y`(Uza{@v0{LTnimijr@>yBB5l*0J~=en?JIStlhq=++e+}WKaDLbmeW9TMvr<>u zx0&<9cu`-FS~L?P!~c_(5@gyiK9EAxOQ-7>;YOzu8q}sWRc;6HiMsPI`Fe1R&{IMV z2K|p{%ft+H89qiGuF7fHrFFB8kwN-N#LvkZF6w9JMX>Fs4pyn43q;!hFPtUO*}T-O zHc>7UGar4zrHn&nj!us+cFa%OAWzyQ+BOAr|TtdTXSdg1nQPNfFZ{qFEk zyg~ukDj^;0L@X#?1rNtLRYpiH*2s|(PkJ5%vnnU2|GI?F53=zTg+ricair-*2XF%_ z0LbMyYy^-7{oX2EKq1-am5bi7uBzizwBM2MUpH|v3dPy?oC-r(`4WD4_UsvukAO;QHQL_J?tJ(@4TtJykklM04uLT4aXH8hL0msDFyP4f z{P{Dy2CO`|_SI|G>YqRV1Oa1gDo=sbHuwNnLP|;?Tx22xG6)VF(m`3-3iOd9!{7v> zZIm%(Zqr1-(IO&ZDE$QBB9Z6!dN*0};X`3>pzHELon;qdmS$43TT@fBY5)U_fhZtw zbgAMU7qIuQT(M$MURU_?<(~I7Kn$EgFfw{-E`)q>xFJD~2%LJirU`Jj;G52HD2NSl z3_>ZOfGhKxjJH}3#BWhe93}xN=_JB&5`@|7y!fKOrinOnnFp1X-M`&mpL>axGpdr- z{OrIPYisLv1hjNwXJGh@_~glDoOuWllQo|`8vs`XG)dgwli)_fU55Mi zIJ$p95HR_v>pSDu*N_+Az8O)da)#c zJx1UU$N&Mep7-$62}k^@-H2#l+zgU0FMbg8fhl|NVPF^lCM&}xT~1;u{&h+UDR4?_ zc2%eLi*VWVPd_YBW@;69*C!Viy{G%n%@+Qg5qM>i9`BH=trFj}=Z}Kq_SI@J8?Oauj)*}-WyGX<-* z(Igmk3(3EVx!RwoeN4clnbGHL-U&UVZBN3(lt9zqaaRAHfklaJJGjNn z-b-uW3aEi&baGk%=A^Q4@pMEOqEi*4os2v`e#t32S*&z>kj6~>0%_gmoycJ!Nn2Q^ zG>4q!n?6WxspXX6g4%^HD$gACfi5)wOt-e^kR zq-6cM8HK;ydjH`=KOR@RAg8~-|7QKm<6ujk%j%l|S6KD415651*4YH_n3R~93@DXL z+uO~1RR=4?G5Lw;+&b8H-@bkOj#gAODtJVcL}=tdM<|X(_sjq~KS< zpm*m0GU-83kU;ZBWb3$l!R~;dP%X0ul>6BHpt1L9^)`y^S@_#63Da8F5X;)@5eTeL z*zp@TY=}(D7jV0sNR|=wx;n#T=-y3~M!?K9$Ydm2%W#-P~_y{b@PRXR7s`P@}2|*gku-uQg4|JL3ha!a08g}X; zg6U=Rmm*4nvw(e1sCMAMDJ)okXB60VGn!Dp0Lali?t-CVl(Z5z5M+a&KHW6Ru5D~A z^Bp$_gvsYojqa4HQ#9a0X3iH0@rrll_9hSdsqTu&bd{a8ztEM~>y$Gv`R0r%qr25s zuV#K72<9wXBcuJpL^=U0DhNpVsBB&x=iYNO@35R4&?Kfn>|a)93n*f5@AiS3d@6u^ zscTgECccRIb+80Q;5il+7CwJIxtaCG za<{lR7AUMe!Ck?d{sna!Kmqf6pe&+l_kErJe1Aol=h6Xqee3!e*rrKZ-sf!%`5NbN z94fX0t*mUKeJU|A5y~4z1ea|ayl`Ip){pE(I-;kjm}!Q94U|OVt@#?;SuEDGw`rp} zQhz`SdX>Q5dEGx#gA0M-GhGH?0)DMB=9uOq*px$%N)?$a5;KN{tlCVi{F*E2^q;du&=vKdX|M&H}crB7> zUj&T3hkbDyvNyQ?fY|W1!3mM)f4X9PUwHg{BlDND^E-dx!s22WLPGdX>{vZnh!X_+ z=N3}i=uocskWL8g+x05D=q1#0{t4DxbNwSGB9e2|6^@1yDBEh5VJ%b|2z3xFy zWv2*%9w#=ZpuGu12g#+SAf=k&S+Y<9;LWt*Rncd2hZ6t?z9|TT>oC(^AE(wZfaT*y zLuW)+`_Q59nKFW200np%rClDpI4)&0O=Pi|T zWXPZ6gz$b%sM)8Vfx4lU9|R%55z}Nv%?oZ&QsT1qb)LeZpmn>3vDFz~R=ima|6hVtegpC|um`gYgUb~;II?2Av$ zYumXv&tEgKr%wC0)z|6eIV{e!B{U2WABX$uPR+x6q--&wOO|5zg6zcS-waJX)Cn^8 zR|}K-5&vt?I+5uQe|8V;B56@E1d28xqKJaK!+R%a%RX-#wL&f@IeGxIo%McbmKr3y zyFl=xRrMo%qv2kBcxV^Gi6}DcleN&(Bodk`QmoL@P6g>m%&(P~k(7DU1l(M??9~eN;!~3!No> zVO{6p=cYDXAkHnYV==T6OZ4;S&%GKci&G_I%>Mp^Lm2;|U0E4Wz=s=Dv{37>u-sOY z0i9;Bw+HpMv%BiLK}ASg5Y)ho-Q6s+n|m4%cozXI{{&Oe9IN*df)M< zf}lbuU=j$TAczkF!w*g@d;R*gB7;E}&KH8&s#datU{P9HI%a@5iD2yP?M3Md#7ThI z95?{(WJ*OG?6pH3C?Q@pGdo-V`P&S7O{qCn`&~vxlqarWi;s&$QAZh&s2^!i z?fTM2LnS$7$WcRq|9(Yf~dP9N~+ zqY?hBeN{y8OpTn#qyVwr{Je>QL9_XbIZp!$3rsfnX9~0?oD19S!i5#!oM0N196aCs z{rlPOm(QN6f1_ipnBKylC6tT>sSZcgzaEz zFHF6PAQIt(n;gSRN=nLJpKyp(x_-n)Y{}8c%j*BJ+03Q7j6(~wiURR~ZYan?NW=b> zO1U7C70z_?pYL``P0>1V;A>CM^{4_M4`jzWxPlk``4=$kN2l<5=)wY^3j%;I8FasOMzH9a=Em%}T5d4QZ=X#oWCOB+bc59dlrO_Pg&z)CpbcmZy%Q36ZKS~kxUdvpWES0d z05yi0cjs!~BcQE&DBg=N3I0h%3~K7_q96@YiD#4Bd!f^cJBL!094M; z%!tU2z_Dor3P)0ibF&>5u>>oAs)W@Rdk|eA;d$?g#3o2I4=<0-A-K6^Cia1@bbHPi z&`JXrIN+@2b(XEQa%^}#3Zh^j0miZgx$VSFnk}Yd zR4m;Wh0cM62~OtfhNt^9YRk**;d(-BD^fkzP}#I`qZtAWOz->duisHhCKE-$FlRBoH9}QRJPLsAiRl$Kzw@3{b%uXO@Gko)IEa=;{;9 zk`Rcop~9)OE{2YXk~y!wap6LfzOA$`c#4oV4C-VsujZ<)co12IbS)sD%}`J6aDTw` zucw6($|nTD*$bkS=p1P;WGvH;-|`7R->q!J%qM+Xm^H=B#T_5}G8YnSdP|V_dEXyG zc+TdTO?CqVPJ%hs;>`X}Wa3 zD_5wzeqpX6MD&ngup_UZ!sX+>5U+N(=lbL9B@z<(E#Khp~RRF`>ojVb7@2Tcouu zVQ!(pZh)wqj2k>N-KCrcE{GVyF*wc$QG3hH>J7pp$s6^lD+@~}bPWB1n4beV6f{II zakg6^m?Real9W3Qy>Nm!i`jI`dqSJj(#$IOR(AM`S| z&Ub!ZSXfw4>{!A@VnN?rtC;;p&^*e#p2i@EJC!>2XKRUwqU+7YPBI8>yr1|p zrYy>r37{Kv)en~WkNN@QjVj9|km2JfNk(jA>ssQ}u3K6C&ZGn+}$;Nu@MQwFiU+j|7C%5Z7jpFz>1 zZE1k*UErm^k+?*p&v2h0pd$HDOlInuhbdYx2Uc6dPh%W?@Sq*!RyX??$$^b^bqacV zLNUTZ2*hK6uR&Z)Br=DuU_ift%N=t%P`LS)z##O6BqNYXcH*sFv&Jd{`TlrE1cPBg z<&iTHL_kMG=_|CtS?Hp7=rp4)2tY@nXS641DB8l7An?^P2}2;#b8L>D8Oz8?*7m&^ zavqDt&MrXT!Mosy2u2_HZY5y^T}7*0?s6O`^97J+(Bkzr~f%?c;ou@qjL+8%J9G)1L}8;&zb0M8~s>RTzoMt zZw(Ii0nbFh=hrj4Y>4Os2W|jRY|{}!du1oolhF&hwsC>Six&b&+M0&a)m35;rQd8p zG{*JubFc*cx&}xRA86cWBSv{1m{2OWxgJuPlZ%Z$^GRArei{n5xDdLVz^s$AMXNKx z1z=*(>fY%{`s}vYF2ct;E-MqBzhJxh*m%`q?RtGgJlF|d9mH+U^nQL|j7d5fUJIpZmV3*y1T_Y0)QCp~y23q^544=GIRDBFUF1XFgUY%u{Fecs`8eXK>#_}A^P zijsP6y(cM&Z!r*n85h}lojglg-FsKYcO<}VFrjKUWkl@^{}Vn@tihU9#0XpKbW$dH z$n$2#5Z_X~bXF#mF)~dV)N$>OQ@S+Wl8u+{m+A5>|18BcjCpgbgGmH@QJd!ePg1 zln{s=ue-riF-=qz5hXNSfoNGNvQsu6t0`4tG(lgJ#+Rt@eQQOEI0CTd6@;FG+f+md z5V{|Go3kL(ro@~``0?Qeg#I}cVeydmyG&d1&zZ&jg{MkEm(=Tev@GNdi8n z2)>}DalzO)+SB*)wX~{sO61^$yG{@NpiAcG^JoBYp@n!Q^H6!zTs9O5AX-GDDN9D} z+S~M_Dk@zg(2od*s9^|D3f1}Sw{PDrwIyBxn=GxB(9qf0dGqnKdTP|#cpCf8a%cb+ zHS0d{_lPiglBGZ&tFV2oQ^WFEMT2O{aj>j+$Cb8I(B!4{{B8Z3{n%YKGo`BQn$AA# zcXV>9!;md6K*DE{BP1bQ(yJ{`rF7;2;ehZN^m%^9L{>o}DgzC2A8wS_*E7KgXay~N zNPTn2RD@sC3{%}9>>CxK0`c7)qPnjr(B5)j^U0#Nqp&!%wSP7=7~5XjAO_@^6F)wJ z=-!LS-3Sy5TsLR{qJ>7IZ>!e!S@J<&aHjLRN1TTfM3Mj~KBN5r`?+S-s%l$EB`+KC zERCALjwVG-^0~8bDJRY-v3E#GksWHbkFIG=iH>i--f_ac_g&J@m;nk1V@*L|X*nI6 z=??M}JTHLY07(H)6!qPZKCF;adN8;R5Mhu=9*58|c&do;W>7Q}xd3fhj#Us(*G{Ls zc~c6>cf75$piXYPjEoA?AfQch9IdK)IR^DYjA|qcV6BTZWoR~92{4ttkj}>4Gmcju zh0_Di|FP8l@4x@G7Z}!W-LnS=Rh(&46O$PCPao~8;)QT+A!_kTPtY2NbMYE!K2XOP zha|tl5ntA?!ro7QvKxUKLK%F10N+bbCz-@CM`i^3G;Y0sk6Y#e%Z^#Lm_16E8N2{s zO>o-wsq^R0k0mj86Fx?2$*o+uGUfH_@0jApLppZv5vT&yeKZy*tlf2L*_#}k%OZiM~zDZ(I63BA6af8@9)C;fk!9vf0 z#gAu!O9uu_dQd&RxdCO~V9DKS=HXar+Yj$gt3h6L}W4{^y!ZXZe7A zTIM=DPNWTsBLD|s1$fIb2JH3RMF@hf3z&ri9EC%N9H6ancv|!^WDX7B=dpGIY%VWS zs5An>8JHF!l-4)F!%I09xbue`6f$J7+l>PN#U>5^1MSE0>B~@ zo0u1jKu0=hG{|}QO;nwB7Q$MnzRObi$4>e1p{y*6J3b&}X@9#kr z3`E7GRX{`uX=#%V6$NRehZGow4iiyAx28pI*I<0T_xk$}-t*kY zt5fHkeRiz9KCzU&?N4P9f=!^OLOW+W6$dRSa$K(tV#}i026v#P z@`vyaMm}=9c#~mUy~rV_Orv!ni^0(v*fJZ=QM@KW!T^%8ZH1{rtwEVhUe8__I2x|o z0Ivczu@0-d$)+cb0HKf?(0KDV79jX-9SH9AhmQe+7O*@`4k@dW#~2JIu4LNp_3OT| zG33_SO#+o>aS@*T>Xn3#rdw&ED_z!5o(*STwwMFchfxqAcgt_u4uSLeZ6~PR-lFA_ z@|>L3;oBDvO%3{lByCMsAccaMB0+A?k^pM-b->*GJQkRF>oX-5(@S`h^wsrsTZc_A z=<>x*`2!re>FE}bsOysD22g-d@y=!g#y)Pmcw$@Y?Afyf92wro>v5>gaJFe%{;bit zU4-5G2C@8iu}fQiodU}oNm*&DXC;*gI0Mv+l->^P36Ba51t|)fq5>chmAmg|zLCv% zRHwp36vgU*Sx~Jk6{399rq_VOoqeDQ$_@6%3_cL5qjcD^(tQv!#eRDbh(k2J=5EX< z9Sb;LB?=pjT{&6Sw3-8PJ5V7ZAEwW%fxH7++|b*KiQ_>#$|Jz=!x#j~(;@T*?rblS z${~eeTtKUjtmx?#&adU=(SQY1SDQe*2ZPaX&m)`S$8fb(T1ZKt8dr)a0ig2e(IWy> z6doTxm0-q&%&26yS_r6fKm80g_~_x9P(WTGw`1Q13(uVzQ--Q(zwA9EB$^NeTeOXr zX|>uOF|^Nh+y)s-Utb@57&Zhja{2fjM|`!*6Rn#g;Q;}QfCWZ~I70c4T=y-m zC+bd4g##lOdkY}lvYV8YnUS%pbB++u7N6rnrLPYJ`+Q%h7Yn{^YrEv*w5v_tf=>v%qD(yJT!z zieg%u^Nk%Gv{AdJTLf>YEd z4pTbuPCF=XNmW*k=w5%KdyS*)xy|@?RYpEcVnr?}7t9t*Qj+pOI_Ztwu5?Fed=EQY zl)FN8MTiywv0qW#eCqCA&0z@4jp%ig>Ib)+O0e@#bw0CesL8A3=@kMSE2k9JY~b|T zK!v)jEZkzS2Cz|*+kzp#;O6EA6|R9HV+AR$_wO$i4FZr2nX*GRAFV>HCY%V217b8%&Vz7R)TIW>6BCKLsc96@Nn9CzWu1wZPpg!Giy*l!ThfxyPV z4wMu?xaTrUT+Bs+^fuoFbQtD>k>WDvaM`ZTfi#imR|E<_fRtKp%%j>u`90O|g^Oxr zJeiLo$BCutP^;?5`;rIZa&ueBX7$cT@yJQb$mqu+Dxtukv>v-$=uvf50Z{Y!4UXaM zD2AmvB}qoczI?+O_+3(%7e*E#3#92xOk__S4Khq9+-T9se5G>7{5{U~+ji;t#3&8y z=w6ioNmZB^!0BR)`gaSLAzEuy>uyYI}tK$Ii=Q^j6yh6$XKL z1oRn)>GXnI+dJux?*r*8ud0cq#^Mi23&n8>HFU&V9Yj?`0&>v}h2Mj0PA?c%VQmGM z<(caPy3*ir0;yBHJ5=(JCJ1=C(CLkl39bsCe#FxsMSK%`LUe)t`w8@7n4%&L!^VJR zFWFL{AW^oZXBkO9xsIm?To}Y?6a!M!H@TqvY3#ReIx3&?U~&R+<%uQUw(o%m4t{=R zs-sp4{sz%p5?7x;>WILp#p>xQDFvC{)_WJhr+>u9>d>5>6_VhySQ|9 z_4T?8Pzt&}M6FjZ&3W?DpEF(g4BbXRx`r46w16RNS;aY4bfY zzY893I`e`d#f~;)bg?etNT>RWJWeS8l?0bC@*)qwH3#MVIxEj&f6#WX+HsL9sGhk; zA}HfV{?(rpm*TtZRm1&;+gMsxvUMx4+{)GexqVPl*$~Nj+B9ab3oIVkdhDW~|AzhQ%YTraJCA?2Xo*3Rzj zYILy{!+vmIo5^>TUh}Gal+0L4?fk7;aHG63KW=l_TXgQ@Mdp$9n|mx>zHum*f;=Q^ zWDD4&+e_iS8wQXEX(n7GA?$cng5vRr+29ruS_dteLvX(AStgOYI5nmNUn<@TRZhiM z%jgK_$7wLv1T4bNbeJG(S<`^+B`Up=M_F(vkkKBy7&&Nfl!<4q?!CM`^Fs_` z&dO~Vmi%pOuP}1Iq*_dVzDnYe*XSo*yYFgg^|rU4KZ>l~YjfTsFaIJLO|P zaPVCTI6iELA;U1dFqH~0#w0BAT6X<=xch)=0pVV(3*9N#&4+-+&{I-MZIa#Jww{Zb zAxS%BZM7xF_)w5{#%B9wQ(fB9tM$ig`J~%L{meb0MU(W*!(Q+d#5Q&w#s@bh`rY=L zaxdduX6q{}?SZ{UfMY-ju-K(#8}oB4K;PjlYr@o=#9{R0gr+>IVCx9rq&?Td{Cs@9 zbkK*3=g*&KXRoWT=U`#+wV6TH)j<4~9|!yd^|f-;B&gpa$qN0qvawh}8FOZjNK`p2 zJ1|D1jF`8Nq4w9xudn>b;M%$w@{{1J#cc33t>k*Ic#h}|2_U6LkA=-&dH$_3wty=s zQFhO1xueL$yZ7!%9Y}-|sB+^zaE4#@>lqpunJ5h75>-%>Sf-7 zgmiy@KZI4y^-o6Vj~wv@xbo}U=&SwFL6LM}6qXF!UTb4Vx4(N1CMVsd>^t0y< z_-u=;Ly^CZuecJ%Jiltj(0S$cxcFpgb8+QGavPO7`H`A{p7+2U9UnLLnr18lv2`}J zoYGrga{lnzb6F2qa4Z<&nHm@2g-7|3gw?n2>KlM~ITo-KQQhXj1$I1Gf6~k#3u|j@ zP#wvulYy2t85^gdn zdQC*$^4pLSl9iSYS5QUXxq`XeprIGXR~Mgu&g0E@Ng{$q1)__8{y~4rwS0>y-$T8u zy|OC=&&dQ6BJox8d%;NVp^Vca+OP?rz~8%jcQoQ2mvy3mnZb2Zzjjcp@YtSa7$00- zYi(7cw7@)>ieIn8mF%c>XWhs?Sr-~gWD1%cmgEeTR1De^G%Ejm!NiR@^&r&FG(YWk z!Xw^RFfNftr6!-p1}zuLq)vbA#0lwSNesj56MorX7UbU=SchfNuW59oAT?&FF|`SG z{dzUb=uYXJ%D?Ym=Z5Gdw}BDN=}W4rX_}BHEtJBT21tW1OCq#tm%Lyxs!_;#64aV- zcrC|NwhKv3hJ}S?f0hf*Z7dkCH5Xhp#Mln5(EOC93lwW6);4R`tM5~wJ_y3IK4+xD z=%`ENJGE&#oJS!%83SopoP`PE-k~{0EC{mODnBMHF%ZvZn=w|PcH{2iBo&T|A|D}% zkn&R0G6zPr?B6vI(QY%F@&5e==v+J4D>hkXsI3Hm=TxoCMK7p=Bowlxb$RH zRq+r$Cdfjc;yH(kFX0i@n=GkIV40#Q`YefO$9embr2{rJputwrT{EGm=SX8lTI^W1j(0A=j z@_+uZt-Z*>$~sx>D&-i9a*!SKdk6BTDOgt_w!tScDauM2wt*%FI=G36iN4YWshRk6 z*|N?toGmW-TwX=>O4I?VFzA01+0piJvKw^F`#hI*L@3?7N3Me<+E~gmx10Ocuav##?O$vqG|PGtpFbr z&cB4K+`!&G-#*yC=XiqcHL35+2U*$Pd|uC%OMGV29xY&-KlxUU|L>3OuIoouRu&C# zobXKZXHR-buGdTu(=*x+n}%}D6109;tg6A=cN16WWE?;rt!GaaIw?)kGj{j>{Cz8- zsZ8sL>oN|@iyS-3-6v1H3);Hm)rjW_bAu`gbXUrqL(oNw>I4^O9?xZzxLkgMB=vd0 z99x)xzu0j(x^?+7vW0_kk~WChn%pS4BEnQrRdQG_+yKvf2*nu9^J(y^rDcYIE@s%3 z>uSC}$KUTn-gPXH3!aj50Zj7?@~E+Dzft*Vtzgacuyls&(`n7Drw&zKs3brg6g z4=L@2X@u|>t=mF9^|7gQdLM!KNY{Q!VFui2RzRr#)E*RTkWGM{i~;vGYD$QlfH^;Z zMU{~t_6|<0H2{U|SrL^iWn2MJRt_#ORyMJGveApm_U9>UN^l!u$7E)5ttO{l3@y0Q zX&tt!%1m*HR`P*a8j@Q++&1$o>hpgmw7K%_j|;(DOxCv|&3)Nfq$UX-2_PhoRHnd> zp%eu-*4dTx`dr{P%GWDcMC%N}Kyy@Lp0BWO2UblPo^U ze#D6Lf2x3&3R@U^2$JZ^hyg+8M?B4XuLR!LvohpKdwhntQvdYnzYH3&twX6L(`REM zmz9w2fg3GC%?VjDZ-H^0mS%qG^%f`XBhaX?D$ffX97j zqfC8ChgCls#~!?FW`~?ejg9@38E3B*$TPNsJp60fm~(x<$@^OIsC`P&J*vLYP35(E z7;9sAKW`3?xj{?lnKx`HnkxrGvC|shMFKmZjlj{m2tGyTkC~{h&;^5TwU8g)4k0A{ z9YTo>%_WanpS0m{egFvs1}5a~sw@l>+?x)gfTX{Y*kcZ*ia;hB85ySaUc9&_?a91y zu~L^dmRT*kBi<~LAn|38+G6p^X+#$gbC392x)=z_G7B~sM4JZd1kK}j@9eApx$@1G zLyWsp=qjbD+Mr{w9f-1)Qpb2i%7(D;=LoSoFy}|s8gQ5Qgg%IRZcQ;9Ixq#^FphJM z1&g#hLsgshk>NRehXX!!$)Rw~zP=9mAl?YHQ?s$)f@o%n8pgc2*32!Y0T~ zka_}2or^D3=T_Ka@nVj2QLLKKTub@#$VxYWr`y=XB&R>&|5~H1TqBPw=Dx^B8A?5< z))ulJXBivAHONC0R`)I_=)}Hp)Z>f!Vww`omE8;2QZKOXxoS5oczK5~c1Y$qGyiuZ zKW4K%l&KsHvo&WnhDTj!2P|MNYSf5qRHKuLV)i;k8AOR6CY=2uhYu~|`MD6&%q|1h&rJwkyB{omOhRsgYxEo^#FaDcp){ES4~8C%@P zgoWFccetqVU}(5+iZQu~y4NP`V&s z!Bm@x;@`h^l@`UA9id(4XhKTM+)P|;^|i;RBqb*|aoRhC`_=HBmOIN`Nf7IsDlngK za7-D@$c~N|NKizLpWtouqS`MlBR_Jnt&H;T!|X}P;ti=b|8v&(gG@#Dp%b8uD>WwP zQ&Ne?cKe^Py>o?FxN>l?&aQGC3`z9AM-HO#+Xl;RW7+pJ5A5`U&msMx*XMJtz77p# zG3l#B7j&mV<;5_pAeRCaQ!NRWF^aw2gx!ZZDT>Ddh13G-@&m^D@q@P>^Tq2G@VsC2 zzF=lErg7W&tr>6kTm71I|HpKYTY|cSq#Sd@eUUG3htY=X(BEmxaJ;_51?yCyd}2*U zT)eK_v|n4AfVB(j*sG@L4;Gj0e;B~@)y>wR-MqWNhzZ=En$;fZ#nnZy_$1tnZ8DX9 zbwgq}v1xNqvr;HhWKA*#;I}>pnf~@n#2#LM3OI{ghtFFSF^RmJ!y87S-5dEH>T1 z5l`js<-i><=p>l@LAW7bkoR)4Qb6q$t-XJHey$sATS4BeGTVVC8ZOmGcuHd$0(j&x z4b2@~`VhZ8ZYsgoCuZ9=C`Y>da7dvxqnBi0;O15o?+fjSA|Gt)-$Q*fdm@ju z<)SU!m?u$fWX#cLCY>_FXD{Vd7h5R?3OGL^Z)++G)xQ+oA>CxNG)Q;Rx?p-ojri|N z*(RQ`FmLQ(uHCMAT~A|*5xFZk6CiLgaM3SRrY0osQ?>JgnN~Txv1#Gl)BSVnb9#zo z-z;ig)*4kpJ{7!=yEciJcckgAfA(cL8vu3GXW_) zb1J77*>SU$F{)4hZ4N@4^ai>rtfkkObK2k5fYj1}^m;>0L4WNQzuZ#I7XMCn7$pxS z7ze1BljD#DY-|*fV`_#Juf;tFh`~MdVpYWXu0>yT$Of;Ps<93`x#!;+UTmwktCwOj zhswx3M0Y!6+I;4euA%6|4H?4D0%3cGf#Lct;RU7Bj^&$I9TZ;MTlmV8YkQY*yAMgN zOVLkE-4rZx3#$;3<1$z1lyyk=&-Jev+q-CR-Z^E@q7sXlLI1zoc;_NY;t2OP7(JiT z^|j~Tb_hCA$~_!ArYl4A^qkp8qw{YkR;nW9C}yO-WB9LlV0yaZ^H@TUo*ezM z*C9E8uWj(u-%q+tw_!tdVXO~F$98a!jf#-h#AMkU!6|lb=1v~78EX#uon?ghcpp;Q zFrQ7blM_AgoWb!V|A{f3Lhqwie6+ScE8SsU+iEO+`Qc=Zuq-D0#sfn46i z#xVB4Uj`5akWWektfr}#wQPFD5=D>>lD!W9I~7>J?5fWgdJx*y8DU>a@jbMbwMe0= zhtp3XTm?Vcc+Yi;mSJO(c11)b%<})}j=fteU19`lAm|tkTES@AVMtn!+X+P;?8?{^ zrw}@@fXbAsrYIgRx{GJT@%gKEcsa%`vXr0v^tXtee%s<(x({2E_VPPd(>F`=JHf2* zEh0^fZ&c5HJwB!Et8puo0=aiPN)8_$xLLX z#EP}adAnVEY-q=T%I@JV;cCwwgKDYHi)h_7ImdDlX)G?C&sp}Und9GkDC}bCQF&uW z`>u{aU;^ZKS77FGa&k74AWGj9WScOQ6|z*CclGM=Pm6JNat4w3+q(x$CoK5 zI3P8nYK%lS8U&pv`};{P8bQ|rO-=oVsAqAkX=n%(W!KznEf~+8tD>fXG94WVgm6tT zI?FjOm1)ytCd8|$kh5w&Mc;Ur%#(e8e~jy`(VC(^>3S#X_#P#^R!w$5KcqX&XyuqVsFx+Bu_^93NQ z4Z=#eDA=}l`z%+~)_T2o0d%dT^z>jaNJ-9X3U(*J=Rtya;eA?KUB(pv&?kE!Ijshi zEYMv7Yzi9zTAAn1o>fxQfZlTQtwd~gZZ60HL4eD|z!0}?yBjf!ASE~t{x}+x5BD7m zKyaKpX8;W0&&A&1;c6JE79gKXc+((*Xu$acgjzYJFOD!gw-JZHddwQ+Tr!nFW)Q!5 zQ=mAu)&Tq`6B6`5sGh#75FmO`Iou~lf)pFMx;qdNYJY8po8E#S_?W6mHi;+EXvLf zI8B5oPDS`Q{JMU`slvm3OPsxorA_u!w2%aEl$4sCpC5ns9^@PWV^VIm!0bwMf|jXK zhVlZyT16_Sw|_=(fc;Gt{{hhcC`e0>OFd)Nz}}UsA+iQiTTr$Fff6JzeT$gj>x5R7 zQFXUHI1BlGHqLje-sU}pmFZ-Nhe*PjwsW{?UUh3L@3M9S@5h9gW+1l$Q`;8Q6E+jA zV`b1D@Cc;)A+Iav0X3NecR0vc!+N^zknnJmyiNd69zS_vW7h#785IrTP``JNNfby4 z*lIvxu3x*hi>a#0J4T|>l+y0r={}9SjX-4?S8#>lEO%+sM>BZFNF$?$Dd3r?YF>|f^X7E< zmumC8_3z(5fOjkw8zp2b&o3V>ssh-<|5WEaXg}{ea+m#PvOT-hHV&MH} zVo&R0+lBx$Iy7`PxP%ZceQACD1aqJfv{c->E?rv#jAVBE_*=JbX?yoKAr!d5Ct5=3 z?p>o*U{JHAaQlW4^?)cV`ytq!wMt#N;%45K;RaBARw0WVeG?9c(gk-`An$`<8Y35; zII|bwCL{Fw{uX!dDilpjW}y-KN;bUf?eFU<7T67i42!%< zxiMc*H188EyMZ#PF9R7oeYp=XV~2lkmc{O$g;_P0oVmms9`cYK4|lkzGj>2`boOHu zsU4`x;Q0U@X7Ap;vmiMN2G2A9U4k`rbGyMJ)0S+f8dbTry---jRBwsaeiOGmV3@yJ zg^7PC3G8@o@S~6R5vaF^3}EmTpr>AXdZwhOkCg90e8>i)+#g42(axypq_QJ86b@a7 z0<&yvwU!_5L(dy{T?{|w=;#P6{g49N<9%Z^f!)B|)6MGy_=rZ?PZ8uaj;H!S!)DQ$ zTbt<}>9N}mG+&KuFm-y11|iY`KWCoN2O@T60f`;ZT2C0dx^8Rkv4imq1kahp93bs| z_aH*BPyMBx}qW@i}b5clwTsyM28E*;E{s+ zR#!95==vd%gYE7eZAB-dA z-1yOx_sdt#T1Xs9KSV5SzXX6hsD%Z}>_AzuQH4MRqkmu0=<%Irxrg9YO0}AuoqfM* zLR*OTia`v}MJ`=x@camb^O*V`oBeY9{CNe%>7e?T>Ib0ivS@)`rC-!;JMhnFeYOY_ z2s}gD2|3t^3A4f%za6Eb!h@+Z_N6BOqL~|}-82`(GXU%wuU!ViOYrk^G5Tk3r-9x+ z+_OvAy1c#pzKlKid)QaCwdJiV_huvg1AxGpY2J(gm&^)Qbx4ps(ya<$0f)3ZnMFm; zz&TpGyFwIIJO?9hoG|yaB*O}B2dlQAx7lEYFF^$T@Zl_g%U}UmD{z6gv`N++*0$l( zfHt(km#P%OC?x2U2-z%(@)N{h!0#AeZf?H2dK#cz-Qh4elfx_+ux>Q`^J(vlToF<` zKu)MIUcey)R`}e~lE!=ON5kUN)6<{{y7L`0Zdhl~;=r5@lOvN z1@uDW-5_5{RH%&*SO88dkhL7_AP@vx_6~hyr6NhJy#wJGU!Rh4!}z1C3M1hou-seP z*znTwl!8xtV`JlIL0zD|QTZUEWI|5dn})Yb7)N^L%tzB}heV8p6BH zEsTxBxb&;uvp=-9CJ>H30nsjK7sN_yDU{VJA>4-E`d3A{I~8F{FLv2ngPslL7u4S> z5XjAC+6Hdm$pZ@|Tn+NX^5SAja?GO|pnu9Xl@}f!jebQ3XD7?9h(7>2aHht| zWotcHnC9l@{yO5oZ-=oEXe#6HYQR_QtUk;(Yzn{?BzUP%_yC2p5^BFnLl>tNu=>Ik zO(1B)5Tn)N|)B-2{E6XI~Ym>qy+;oEqNa^WV~VdW;lBm8WOcPQn0zLE&SQV3m3qVR(~H3v}?wd zdw6rR<56ga&a9`tFsDI0-pbPmmg3Ri6UtAzM;es}^z&*R;1{do2_*=2jyKE@?A-v1 zT|n%B-E#Z>eIrWZkr%ZF-~&o3fRNwVIRH@~N&+WIDS1BNX34LZnSu_!x&V6S?c2V(WUB{Z z?T6!a0uh_VJMg7oWAo}F|HqoZ{|Ir2sZ~_$t<4cd{Kw@8u6{83~hA^?Zx~frs8HqB>>{@9GC8&CK#~y;aoT- zYXxL3KP3rX9*!7dCX}Ql93*>oQf2CR$J({*&+%7{E*U%HAGk36co(7Fcb%|}E=?%1 zy|hfuN&f3+-)Nor@v}V#xQq`-x;1E@oaq1A7fKQCgrtR=j3bBkcUqmpgoGI3xZ?Wj zw@%h5w99WiAt@MUHXX_PeJNbOF6HBwikvTEM(<5@231`y{`fXR`}FUxzutU9dVNFb z=NqM0p8q=E>F-x}eq-mI&4WLiOY_R?lO49l&dY1mJE&dZDZzs^qd z@z7bq&Fvhc7CYbkkiD|&G5w95Tod8-hTg*jF4aTi%%0t1F9@gKIo_1~ZkD|+;LQ5- zUa&k8C9uk6bnNa!ru8O zTjNX?c#SE@N1h$-_PCR5oghE1X69YL*0sM?+S)_a!hU%(L_z-KGy3Wh9!@K^-`%IQ z6lSg;18THQR{!=la_5+`+~MOa>$Qd}BP2PUokJb-WkNS>dxc9KL%v!7&3)$})BDwT zAG_4vB_GmgIEjy@9=zXYZ0~m>gfg!qqx9RCNK&hky=)4z7xykn&bbKbc&$!Oeqf_d zpj|sZ)$p#FIFioIu}YL>_UZ;-wPHAzyptx1E#+)w$)(Ar@bc-YgILVu!o|G@*<{+I zsd+g1v!)L?#@o@?mpCN|bH2T7rHt;2udoT)WnQ^1DlK%k{nnY|xaL;V;sX19(ar(r zQ7e_g*7oO%x_7RO;M5~NDCX99cgE(Ln18Pj-?wq@W?E;MC091<$Mn;v?t{J47_p_Q z{g)L{o3H%drcyF%#EAu6b@^uCp*xc*|9gb@o%yr5;Mw2N8vPvK?Y$?l%!%p_)x|KX ztdCB{wrFh`$K)X+C}whT$V2s9w(!+xtD2WRua=t)b(3+mzOHg+wI@cPo#8}|TdliZ zed?sWkd>5b5A$RBr)vL|9%Ne9F>^DE^;pI#*5U6iwLUEA#o5V*OBGA^{F1Hps)cUT zBQwlGF|oe~W!73w2Yo=jWd?=epcWXlUT zW)odKs6T&Ke`*|c6<+?*aOEE_dH?ybUJZ4na_=duN*V$ zzx2qr%^7={)vC7ht_@&=NZ7Pm?S!&f`^A^oxRX0WZB+Mbo%ixc!>Y9DG(Ca;Q)<{O zsNT#Zqw$2-^pYEB2h0s6iD;eMo!v0UkE;bnX@*<4@C<(t&l_|$xW}K%+xs>@P)o~z z^0gt!KW}#L@2MJwd;WbYL;J7jf=zjAjc<`DQ=vqW)3FBzU})8P@CP%^-1a|^kdZmJ z5Nm?9G_Jv@+QG^s7sDR-wYm)N&6?W%0@3^O{*?W~TN0(!CH?M_xY?J-SUT`M25FUf zYzqN&9`aP7b|cC4b?K+?9#DX}r^zliJ1=Z2sU<(NsJLbL`?zS{6}XVKqzu1Nu0Q9%IJ;mVAL1YRF4yPl-#_*ufoP(@8wX_Td z6;_kNsrLr`B+e2BZf(heJ8epse88Ce*D~9;_4ML2%R0rh9y+H?O;K$@gE=jND(it1 zE-lRa42miDg7dpKF@+#3H?Mii_S#D@x29zFU2&{qRk6UFn%q>SKwP~;yq&EzqsyL{ zVP7rwS6Wepc};%96wX=!H&iq&jK)$)SH|tPz5f5A`h7Ws3tb`uo1-RxPK~i_17pK;U6extV_z`gm?@;&L5%C)w=s zP(+~D6uN-@JtjGQ>`UN-<5D6i{Oa zT3~wJ5%rdJcMkIMdUg8sV}{zgW-W!?IHY4)3l`t~^@6@D83+aNj>{j{Yze6~mt|Ph z%*N!CsbNBhdf?%YTc2Jzz%5|He}~;#06RUGf4pn+W4&jmx@qzxs<(AH*9S)~l_An$ z=E^D`=W+>q!!)KP<&Nqy16|IVUvxogcY@2nDYS7m@}&pVDr zF6FADEz2-Ay!W$s1(GgMh)z@zu8c;VLPIb4z3f4}d0w^Wn~5g!iL#J)i{_e^&iH;J zioX0CscrL*ZuWbVzE)H=?MP7mvtR$XOb8CHmKa>fxHp!+#039IEU=^Ri5pfl-!=-{ z9G$m$ms!?1AHY=F^fo1@Xlm*OuNSRAv;bvdzIkVs30zfIn5C6cmw|PGO!FFgJ($!kZY{`^=Q){@pz2f=dBDj? zSfw~iP0TDNL`&5|u_a==glTmR*Cda(X8n&PGp<#qdA-6suGRaCQtQ^nILrnP|TfohSAokf|Hd3x?v|fE}(47eLn}0KBp)(We$B$QEt7hp2j*@v*nLQefnpk7M`3!ieH6FOL8*TN`5V3I^AJ|WQL6+Xpkf9T}=K`UyUk;y*gD^~ig1jfKCS<19 zyc5ezFG1m5R3Q>KD#`P>-=-7J@{*?eN&-Hxdwi2O>tLfyT9Sa6{ zVq!xr!{IToW;`%)R5P-_hH0{4m+7&FE=$RyN^wKR*}UrVgHQ&ZIr$)Vnzvtb^}efU zN=mHTV0)ruG5Nd$gNdF;VT7UeTpp~slt@MH^Z>##QF?vPuUPpZz2ET`2t6IG>eN}i z3T{zlu?&21Y>es-jy{%e-hEK3K3oD*=7b+1<>no3g2`^GMzBb9YF5K1{vymID~0xY zYQsq|=9c1TUj}SKpR}l)g%D^pZK&7#KDP*Zz_VS>|7@F0pl98SZd5&KZy-dZ*%yT# zLX0Ybbf<}yORn_i#$=?fhPVCET^WCO*A;G^t52?Bt0KG2dn0L$M01$eJw;pm$AAq( zxEI2)``43jE8H(2AC8PVb1>CQ#pW53*M2xYaSFX&)x7z8{zZuVUW-nrU-RC7!P|6Z zEdq(O(6Ih&_=c9dJ_%bw6s$;3O6JqtgCh*O7k3%%x|t8 zdj382=LNVuA|vW?{t7zRcK)*J>Q_fkA^<9r5&HK|dHN_JySH{se1Hgk#R|#%ul;z> zW~pBfxRUgI-U!{Pp^~NtaN+EfCVt<)w#&9+m!9NVTqZI=p|Y`@!RHW&uI>MxZ;jUa zyM~rmsICYQwZ7-FPyKr{K4H6tE>#CH7yymEjx!>)vzH+;-{_+S z2FRPBPp)}%J5GEGpOc)BFx2Uc0ptRJSrDAP&C9dZ*4BnhzM5L+rkE$7M?R-GbpR;p zm1Y8uBm+qPL5kSO!eVAs6UyGe7XioW>FI$#Au(Hor$zLMYW?Sq+e#KIXA3~ODIX^! z<9U*bYAv;}9HhSBha*IXgP+{BYuEI`M^A7N1S1dz3GWT1cRKx_p3V0-p3=sHsL=&G&l-pAFobA zW6dl$3Paj!rScotmrak1mR>#9cTY(PP(75Q4P=O+S{2wrLIVJL51DF!0_uEDBAnEM z(f>}9EHU&teb)e2Qp#@7hzkYHt*r3t7Oo4B*UQd8%KGT3Qz4Lwjf|u{c5JY}KSm+{ z!v`K--tOq@RDf>42_RpJDvb2;Q2-ZMNJh)b)_}`%&bUE+l-YC((9t?UX3j^c2YB&VZv|6ckk5275!n2cLFF$}DVo+FfL4Z{`009?<0#1Y>YPf%v?< zmCdB(j}(1?LP~+##0Ur+tFr)JQ?&uv36`LeA^DUL`R@$U=2w1tmpqd;c3MZnL|dwZ zVRew%6(qL+8pTTgL2N-mb#-khH;5{Wnz+Emy7{UBYXArU zbOlLxC{7g_l(E|mIugJ|@ET-fZI_@XrnI~~`JqGg6&3G0OuJ8-8yiEOb^oD5;FFhl zV%pvf^LY|I)6y%6j4L?d|D_V%1Vnp`@k;6RP`KTCQ!ztUxrBlzido$qEvdqj8`P_0COZ z7`sh0=xx6d;a9F!frjS z&|(pPX)!ulmk}6OHtB_6_NkL;*2(wEl^-g(-R0&^-O>h_ZT=ekSW$NyfMD0oRcn9n z^P}*xt~q*lAo}gw^I1-+s$ud+l8#y!yYahh*i}C^gV`HR6EzCcrBu8?d<~5P8rkRmrKzodj zzhllIu>;0a&?lG-126#Z>B)c^jGW6_-I0I-Ws*E_aVa(hVK7#=>;%Bd$q0aZ0rk4C z6w)MV1yBmmAA;t)ciXAnhOUotiUBvziYzFF(d@N`^X6zi2z;MqRj?2=C-7BF0U!=a zaB_592WXIuNYlcE4nfkK;Oz1V{f(ZdomcFE&_4pG30)qIiHkk1+neT^pZxl}^H_Mm z_Lq$V1c9x}QNX&QDj5@}|_>VpU;H@&tq|L?N zzgXOjSw7++YcDvJb`tKQ(=-=oFND?IW$_2J#DBo{Xdm*X&6+biD{J#35A5w-g+NtU z8P~;4nk`RuU~iYz0CJnzq`@QFj7ZPpJ%k`0#{uHQI(LX_5+80I)Vu#@z;>#Fz+4JT zbM{~5237;1Sc536?brCzbt-2yLDX9wO0m9VMtAvP9~~N`Lp~lJ9YDJRLJBO⁡^z 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%0D~O6q51 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 HJNJJ8BCL|} literal 1 Icmd-A000XB3jhEB From decfc577ba0358ac4ea4501fdff62a83e05bb945 Mon Sep 17 00:00:00 2001 From: dimiexplorer Date: Mon, 9 Sep 2024 15:35:04 +1000 Subject: [PATCH 070/369] chore: add file hosted-agents-git-mirror.png --- .../mac/hosted-agents-git-mirror.png | Bin 0 -> 52848 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/docs/pipelines/hosted_agents/mac/hosted-agents-git-mirror.png diff --git a/images/docs/pipelines/hosted_agents/mac/hosted-agents-git-mirror.png b/images/docs/pipelines/hosted_agents/mac/hosted-agents-git-mirror.png new file mode 100644 index 0000000000000000000000000000000000000000..420dd68380c3579c83768066246fd109b46aa428 GIT binary patch literal 52848 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 literal 0 HcmV?d00001 From b4c1dbdacf1d39706baa51c78b22fdf17546a394 Mon Sep 17 00:00:00 2001 From: dimiexplorer Date: Mon, 9 Sep 2024 15:38:30 +1000 Subject: [PATCH 071/369] chore: remove file hosted-agents-queue-image.png --- .../mac/hosted-agents-queue-image.png | Bin 176129 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 images/docs/pipelines/hosted_agents/mac/hosted-agents-queue-image.png diff --git a/images/docs/pipelines/hosted_agents/mac/hosted-agents-queue-image.png b/images/docs/pipelines/hosted_agents/mac/hosted-agents-queue-image.png deleted file mode 100644 index 0d6f680671a8bbdf7f79dba7c511cefbd0026a37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176129 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�@kbq$=2e2r60MDo-H{FSNVQE}54 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

~dcLGd!-RO`q`OZK2v$1b#iS?kj;%i$=-GoJ| zo<48oPXm=5HH%-{G=17Xh+J8vV>1@37#AWa{hs$%p7YVdYs1;%WGpM>q};nlrHZQ`{#+>FB*V?1;r;^>K$jBe3SLD<7IX|?_hCA?7-B@{ze02mg|LFbzIOs5S zTBOVKv+D?82zjctf`M{1W(w3d-s27_R@sc?So3;H_0}`y>5@KK2?chwd=~Gh9a*y^ z+ny6MgNsM<`lfBK^|1{E7SZH=GQ9L`lI6a1oNKsQNE=p8n_stKI738ncuDwqHcVwra|cf zU-22o7qi2wVznA$G;yMMxqw^n?;ZLXvZL zaPE8SzIE??hglAbw3BoGf0u9X{e594-fFkyWdyuDZ3SV$5lA;;CD0v@8cHoArv427dcaA*2NGcm=Hh4-? zCJ8A-!>R3$d?wTVyRLS3&t7$o(>H!j+=YWxKEvTf7fj1zrq>)_@Cw~F8ntMmloB*rIe zDgxc?JNv+iHUl~&+OwpX{)>l*dO^R<(y50ql6E=u3Tq!uJ1OEP^W91aSevyq)J32{ z>*!}F;RNHr0?Rp74tN%?BEg=G(J5BPpxAQB|Is3!SDuq^oK>sod0siN1hh#0X^d01 z)Pdc8>I}$j0#=Z5ZzOj=6cYhZKeD4+=hioXIl+Ue49;w$-s=kel2F#&zuvFaouaq# zaSy31e186*Yu1HZvI;-#rXLhD@xRiqJcJcDp?CkH)9%TSk5?#0)fmKcy=F&zUnKpN z{psS{0CN6Vbq=#3v~=-cZ}gjT zyx*4!LxAWhqq!OHRJFZOY)jY~c&H<8xIg3ot6FimGr!pEty>0ZRf3#`cTb64Y5dQ> zWtuQq;o|El8)yBx-2^z@bN4JtD`qlNa|)09GQ%B1*TzAdw!~)Fb?WCS`0clCtXyY2 z_65H3zgwUJZP4|-?WqFbX0DwJdCkQqN_pU|Kx_jVb5}_Rr+&363dRN3H~prZKEJhB zuStC1QMsC0w;s^NLByWG!fghC`yoWmON|J{deuu~&yXumVtrN}gVIl2rrTeuXv3$( zaj>#`189@?Y+YzsCpdKj*o$GjNHK zE0_&|HH%gxiOZ}hBykdp#E+efoKPH+v(_!!X){{i;PfheW3sW6 zH5PNf?HfTS=6!9Ha)Bpo&S*Ish&ay$PQXwQl)GJRg6LO0I{cevce|wYB#HM)F?36A zp>lkgwi|{nh?CKbE0#r^#$N`o2Q8AlZflKadjwlJQE#jjDDSi=;{@orK9(-)>+x;z zR%5Vtu1@~^vcIjcC8Vn~P$iJL-eZ8*d2MyJ~^|Mgz~FZD*yHHc^i zes7;wsHOh5Gq3)F4y-Mu(4*k`t72gK%F%Oa6S(Bv;QlRG-iuP_omW6S?l1fQS6Q6o zL*M|}1K$W@UiE!ND^+w(L)U(|;=QhvfYIY?bGXi$SxPlw8PntP1w+Aw-Cq0xl82Rd z^{KGIkG4mt)3lw&Q|KOSg@UJ-34yWpLH97JmwydKGRrs5dVBx#-QF3&{GH66A@!Tl zeaUsU=5}_|sf!aQOphiTh1%Q9VmYqIQOb@Szwu&-fJz_z&Hc#foK?^n8)Wzoj0WcP zJ(O+40V-Dd&h}XAC;nZViNS%!yflv#=pjq0kt`P4wjH>BHH}Fo@h*6C&Pb9+$DP`4 z&{r_S^=xxHE-EFQOHj&DXZH4VFD}PxHul~e8seDH*=?1^X*Sai+Q~*egCJi(l(Z5O^gKQ$Zagu2&Iw3J;0tknHo&OdJOoYwVwd&G7C925IU9| z&zbtSP2{UY>BOluew#~ySpn+9V_(!nf(tdkcb8=zPa3^*vYNJf#d2^~%}G#haY|3IkG&3rXyW!yn<-80NEI%cqb(xn>PQZCct01yubTZo+IogqPaT<{2nL z7|r#(&O75dz+dNNTsYTXsha#?!AhKd=ezuRm7{*+{)M^y__qN-Za6f-ucu4UvU2@~ zJUNv&&+tRWoE_OEThd{tYKyy77Vj}(tHc}|_{;qhx5RDxy{a;`sG4^sMTc^Veh?~C^tq{@xkkLL zP=LVQd3Q41=kL|F2cw5&;%I?%(x~mghmM!eN|lI?;lt!uT_B88ia8hP=8E^JHvL>6 zm>4!GTRY}{kMk!^w}QSc^7iX-m?T8$JedtwE}MR57RgXz*1I-|dY1w3tAuJ3S>oK- zmWP{UGGzxnW`6kMAk<$Gt~syG67k-&I0KT^(4@X+lX}+^7+`;YVmoe?3o1VHTXZjwIL;5P>n5HGhRt@+QBskEqets1mBf2e;7}k4^Hnx{kHz*L$UK>8VP){Dx8`C#oPQ)I(UKY3 zOEyeI(0-M06Sv>%dJ}F)i_^zXp8B=xc7Y_fqwB{uqFjKq_tzCfOq0NZ97*39d7B@Q z`^8m_!@TWwaqcJ-Q=kRvw*e}>id``H7CQP!kbpWb1sxnfIT@~XFKwTuyJUDX3_-Qu zb2^dmUI2RIpRJXlfH9^F0o9#MD1*wXgNop2t^q>x!{jxlR@#k-F-RA#L z7$@$6$}OZ&>rsHD!U&{^obgCOw_7{j>DV3sj(!)%ORbM6)S2Dm=$ntRc(+QeE$Mrb zE-26B>K@@8osx^n{90L84LE;AAo^HyWDon`barpibF^Uh$h2)%8uspgKqQUG8XtQVY&%C$1#79F4Bd4wJ4i z^jzpF-HG!~&s6zo7mR#RWYNgUn!!aB#ft0jn<9&coR~6%1p5Fh>31;e-|7;IPG5Lh zokFSExi+y<2Z2osg z7Vnw>3QJ(d_Y@r=>eX3}w#WqoLpYY+HiVj11p8nl4W4@%_b!RFMc&y&Q|i{9@bhF_ z`cfkys6?RukX1)F(b#6cqNi|$bNdvI#~coqnzqqM?wu&f-TU>Mlh1hh}m zl=U{vpV*Z*_D`CJZ~KpJX}T5u32s^Vz+IyydB&cthdpnmn`Ijk|7m!Li@jql9-hXS z%4Z9VI%WLk8A`EZK^9|`ZCe-9%hEz3zx4S0WSg|Gh4IYv>H6&4h%NUxv&`kf=e`Al zpU|hl(nE@Ap6pG}76W}|l~-7%<4I1eW5-u4_(v38B15OYL?ys04@t*z&Jp|X&#qjW z`)mqn4XHUpp+3sTF62EXJ}edLpoT@%tJIS_sX$(Hm*L-?zg^m<5vV?) z04H^@`7jzfFQE($%#u+20&$2W6PPGjV&8{BY*Bdpe9I{xZ7Lx5Ktm1#bb|Txz)}aR zYfe)&3C{G$%&H0o;BHVoG)OhXUQ?Rd*331Yk_h@dV?(K* zw-}ZbYja&Wsj+?{7&Y7)JfF~1&QIQ^kD1?7;2CFh6ST;j#WRH|7LV($}|$Ephi&zWI{EjeDpFkp~c- zi9p5+X+QaRN)uz_nh;onJQj3B0AXk}3+nPoBQQS~6t0&vROcZ6Wk3!88wsPmL`{1q zDfjewT(*R{T0n0f0R`Xo4G;E7NC4yLiY3-@JruOP)!Cg$fX*<{jPoa`1%mQm)aG|z z7Tp4YS`s@iEZO~suRaQU6$N>1i}dwuD;<0tfgeLCf6jSmN(*jLx|H4STS))3C-9D& z9lawiP?z{M(af=}yF?e0u10ny>ULSBw(0V%d(#|!)0_>vFEQ?6N&%OsI2hS6gBR*H zhkiW@nO$fJZ{AihQ9F}re=e3;IO~hiz8)F7Cb6u0rp;sdboj*;9>vW%f4E%dyXQIb zcGvYulnoT}0Y6=H_7@emZ=WADYtwEob}MZteSI;kJ34Y*DfrnyT_+1m|K+IZ+c2tS zUkFiP?8K)F_pQ0d8IOB}I|>C7L~WpVO)Pv+86tl_UqWka3t{fr@zm-4OGKSpJbZ}F zZXOptRJUKUgK5bwABKLt@`PXWS7fQ<#@8p-w^kVhOzx<{7vDXM0DT>^%!> zmu@^QrL&TIF6>BaFP=laSqH$=+4RMKt9}{G)K>xobwbiJw;>XDq}y{eu|~#YZhy>8 z_q?BEc^V_O>Ojo(7rvoV{r`AteEKTg(+K@IfBZZ*ANI}ID$Z#5ls%UDcd%cyMxMf9 zy=I8)4N4c+cKiJ72>tqVKCfgkt0OwDFoQw+yjef-^`Ec9H9no9y&N;5XdJS7x*pFy zw8FHNQMGa55>}S@;_2bmyaEDmT?1XBSVGyj(V@nCoVB5Jiz|mPG)}mG?HA;I!JXTK zIdmwiy!H6lj+*FWZyGEG1HN0pqV?e1qoc1{cVCjDPeW+RgF8WnJ5dSEEXskQXxDY> zv}>OK+42Hy)|Eq|F{$$@+oY1As@5~992+JH05uz^KuJwZ4)pdW6G7Y%dI(g$rg^lE z=AU#5GvQ1tX46b|o&N0*S5FlW$j8r}GD-R9(r}$uTlcGFr>J28l(3(?h2|S5Af3nO zVtwvIl&QE|q>`<{0#hTZe9hdL`Z9il%PuYa$8eRS$AUPAz&cs^4o=mv!qZ-L z6@Omn%`b59Oik4kv7fY#KCO&&Zmm2gKQf1%vGTbpjw+=NG5O>Juo3R{uy5FMn`EZq z%TiTB*!l&oS*Lc4+iMEDbM)|Cz4If!`Bm8W6-iz^?ZYcPDo?m3$WKqDv3&JzJ!wyu zg;~aJTf2nof=yroSGXs_S2tLK8r-60y@IHtp%(X)nox`!omnt$-x1_Nd+^874Py_m zt)qA00P`zRxk#@BP_pA&&&gyX72el}mhtOMex3e}aZnE|?A%jHDgS?5uQm(DQeOiW zm(nugqpcl&Q#G!-4}j>UYCGgYV9kIz_|LmA89E_pc)w9j&v6wW;d_e99r%GMFpVCy!CAsu%7SPhc`k2ZXa%`{K8$9b5HZ% z6**${+3m&U5zsc1kYb!|V z9dTug2T_EslD)6&cJ%cooyd^u=*i3S2!E_SyJHm%ya{ebm$3LwUey|=FKkOXsIy`j zO=Tyy-_x{DVBa#$Vf;fqQC6285;6LZhmL<*PT@4i$@-d@*jFv4;h{fb`g23U~CzW++Wi^9$xyzerrn` z8PFx4+>3c<=4#lPE}PK!`7KA@V~g!o-{ZObyW}bXSX6HhGe?r$Q-{_p zMxXQR&~0ni=+9J1l)X;gH*nVV6IV{tRpNC+;$KN)UlpYnDqQ_izpvE7yqmQ&RL9;x zUAU5Ftr&>&b0k`NerXYGa8eb)ni(TXJx&&hK3Uo$f3K{{y7~IcA{hP}y}&H(Z+SB7 z!*s3+!YpR~v2Ic>EzFV28=1jI(j!r~x$7;ca%inT^~PTE{6@xi^p;*rpjud_ESWWs)&wp zT$}_%sUuXf;(CeZ%=m>BrarjP>_yxqUA#nBx~xLRB*~5mjZul^V7ZzklG;>5ztQ@L zyCNAf%kRJ}6i{?aO3!%8szXkxuOIsL?|-+&LwOE7dy+{BwGp}yD?x{$Jy1Vw7&>Gy z{-$;GM@LjY0!O-&E>OwM>+s~H*1{Z3aeZjSuFosM?m-6}%*O5b zJsSt@j1V7nikjGd_e}A&;}VR|(D9mgEkE>_&Xfi}mgd*u=z+|)RFr_C)X_C_Nj12u! zP#D!_Ials#DCzHDLg^g}Wv=5OH=(}IFy|tzeYpL1n&87nMKcK zwovq;gP0yLWCWoDXZfGzEr^hpQp z#^%&nD6ENkFHaSJNRBD$+ZGQ2bCL;E6m8y`W=EKC9_#v(H2{cS4#rh>wWm;nkO=k| zhCON;I|gO%Ha0fmP{6Kx;n!nXb?CK$_B@O2ziF+3pxx>&j0)dUk5{4aFN0i0O>Mv~ zT!Q*moW1ccz!V|IT}M%}e>^7<%;!`Z*y~%D?stQXdX+6&DtVCY)>$P3em^h==gnLP zyavCMwlg=GtgdByB?wWX3dDY|t?N+Vwev3bWPHgOk$%+;X%%!0tqB3Gco4x+EL{WR z&^H6C#5;dWP>A=oRdCczq8b-kxa4d_^?wXY|NT$Qa1^uvQWymRw%eh{3nurpcm6B8 zvJ>~}FM}*|O$`n39Y77TDj6p2dnTbP`tM^S?11>!(B8d2ie0qE_FNq>KQMt6^bS-Q z3gfnu$%awy=Y$!wOS5#xb*pIof|kOBR?w7WyBE^>>dBfAyc0Kj?!f;q2;r zYXe37<13?-vdF~dE5LPW@CzCcq4R7<7Ia8KrbM>&!W~!+Dg|vmLH!ZKE+B@f_O0GX zZy=}R9@_4+Lu&K0@)YjRrSTM40)RxWXaeB(Xvv8LU=M_Q z1U-%|&X9)vG!5#&M8}CcZUIXZ`vkFTXIn zgLAAC0&^BinB;jfhkT75m^DPZeU~9^e7DPDrsT3&TeEpvb3sdv*VgY=;~Rn-={3sk z1sb1vCg-6iEQ@OU7j1h?>xbTwiBmjlTA9jT+I`}NVC7zTmb;tKD%jT0<<6|~xX|_7 z!_ckArT0pY+Q_`c2DNRY)HvW);nr>Yr8{A@tEXXSyn+ReKW0Ixt*@`ItBcah|MuAaAaqjtU&Dzb=pE{+lC7WDf3S&vv=CR9AtpMd^yJ`?JWA> z`)4xhusE(}(&2!0k~Qeqn%kA$7agniep4~tV7Vseo#-%d-&fnv&;TZ|wwA}vAxsVz z^fqMGE%W%+x^WdwtOzL8PP$&N>77hfmJ!!|=fM&>;NmMAXy&R-8FMyGuagOR)vT=t zE6*JAJBxipcTh#GK(*@JuV(RvyS@nQVr5z8+1EbZ8pg@cig?Hh?1=STyU@R*1pvSZbfK(KYii!$Qp7Qti z=Pn)H-QBrsY&B(BN9uL{yc;ULaDN8B2FE$F0tIK>tc!hwesuLzK_fcFV~B_DxysF;(%f+AweO8^#+}lnQ5fmeN;gKre18Qr=YVFCRKs$={(k} zCm77?eLuKuULj?Td8s#OHnCr2f1m#HWLiMVtEBowdC^$tkf)(xSoi*CmIXkxo{^CO zUs;0%RHZ4)ZO@&1VDd_|gI#|KW@0Hm-q$;+>q_6dd*sv{_;O|yteaK1M~!wX;EKcf zuVaaRwjTTp}??%Eao;>8P?>3s2``j!TW&_vQ; za?@%BYBbjs8qM6@W88I<49$3R_41$VlmA?Sf>DX(;r_e2xN6Xq4yVbF4)l8?zu()X zb{*>aT41QHsi}GV_%U!L0U7T5?Cef?^{}PG@Z!Zc)6b)#qG0s54kK)(QX3j>TUsoq zeShp>dJXqtnb@{pqMrlWVe9|rY5#YZ2VK-3OW`}(+KT8? zf!_V_c*{<4@!06-Xc)p`mDJW=l)_tFzpkL>bQD^dT3yq0aX(+lsd-$$lI9mj+v8V> zh7&^em$zA}PfCS#*LIMH#;ez%ioWV2V>{zdJFZnVEzH+)*uX3;EyW1oO?@!>;%HK37O#(WMz4t`laMb4N)#zTCE3iF!PuVUV?(W&5tK6fC z=IeBo(IqpJUI0VECRv> z?%Pt_^OBOD!J2I9YMj(~pYcoV-(Y_{O7tn@qmz>zaUMAh!GX8+{x9EXEAR7{p@Hkl zGL9vgC8N1V=WRG)Oy!x$n^c{SITte+I-TwdH54)kLjB$U*3{L3*#z?dn*g*9!C+*k zr$^3ZFA2;Rl$DhMoh$GNzYl@U0<(@6Mh|^abE<1!%~Egry@}OBSU!?u>k7xMw$MQK zl-%Y@@b=DoN>+n=D6j5##9ivXoSAcN@EOS9S~u*~@tXjS18Q$fL%ha7_HyZcJLCVp z_3gUYKTol9$MA2XXvrr>Cof=beY@MNvqO%oHZ66(d0F!tTJ378K3VPLcg{N9-uSsj zoha)y_i3K~3@P#0oVlsHT{eqfx1Dxw9t~NNFw_E#-7ed}Nno0Juyh<=I#QR8Tmzu~ z!aB?tN!^DYD(D6V3!Ca{;{F+ERXVlu=OTh^{?MQ5V(O89f5U&PzZFFG#JTnq6c!q8 z{P*L>OQHQWiao$BrMf~zOUu$~yQ)GVEXGTsV8^L($mVe@ZY=p=BngIf|8gOn9~^*&57zP zZPlZg%D$SjJ;iH)f6vC)INYel#?sP%aeng2lMOa2K@{LGs{?A5DN?j>Du`RebY_L#=j6}ieCSlaM+H?rnTB#YjSuj*C}dux>ZetB{|Nj^LT zrfUZW9V}PofXlww@wSR;ZOrjli;b4Ts2wznopdcgs=Zl#;NlGk;CWuf}%Dr^GS z*uW3)1`9hCIze-b!!z%vx&DgN>^JmC_NSB?<@$DQf;U^X?C&qDaRF)(!~Xkz)tGrr z#!C3tmjuGMHM@K{GBR>dZ-3E&5%9mN7qFIat^NIB-`}`#1B#A{4+{f1Q%=eLH-RIf zE++V-Q=_7I>;;Fve><_I0O-K~>%&fqg}g0A`+w#8PffYk#V8BPWrQCt`wJ3rUe**Ufj1s_pc3+kkBK)3wN>@4taoCM)n zO?psU!|m?YRGCH+8e99KChCas{!OCY_n)f;Ck&hNw=)Z1sdII^EY+M8ElaD*ska}5 zE<(2>Tyv@I+YgpAfkq_k$&-s0FFGtQg-?Sq%k?aWulwRo=Pk(;n(Y1r%9zjP)!C%& zziKc^*9@6mnpK0pZ$SJq)9fkj{Y|B&`cu6w9v-oCCQ+!pujuXVhs>N$sH?ZPw^#ow zgs9DM;~9JSHc!IB4wu74(}7hEEJqM;iL2NSPL)~Pm4Z%&QStGA;8!QU(GD zIfce*Rta1+;GlqiA-qmcKMD%H@U517LFdi98kSB`hV9Lo){j`#2X9yT4xdE%|4sX9 z`rGB5{{_A}AcuAt_?4FUmGs;_6#Q@HA#>SePnZ+0YkoLtliIR|}Q0AE*c2B)Wp5)}u7#8lr#8~!C55eDkQf`46OVYE2 zP#ieA%!9}ugtHR79IGO`j@sy@SkbN`JsNWX;X3x^cvye(8j5Y&N3h7UD$2FzbX3~& zLDm`(&U5Zy6SRHD+Dy^91R)>DNPtvK0gc9(Q$&fDva+(0QWgYmt62^k8h~CA7NPzc zng*;?AX@fGlk$Q4{Z1$8bX#OkcbsSh@b71KmOsqX>N`C-P&|Ze3ZC#I$a0Meggn4@ z;*LD;`N!e0jf+F|I|L+UTpk%bj3tpsK%xT2%a@;l(+e2M_+11+7w8tiri00?gR+j? z%QttvT5ePo@5e%&)e+h1O||CSi5b;JqNM60L9)*1rO z_1ySlYVb7ndRg!7xP+dWAu$G{=hC%uHCY$$wpA^UMq;p114^2aML8$He=w^j14fur zw3gLxxBYt{&&jwP&Do`pndE$4yazNUY}^(Ghy;$%eM+jxMBiXwYWiySz*OISYHtIp zvcVu(k$1Fvdy&7-;ALs|coUaLLuKC@sO82&{4V#t9AN~TJ}r-XNous<9gW+;=FO7j z2xXWBbgxhvmU;1T_Sb~a^FA2&Y1V@3>%2td2PdLP-qND=Iqw7hcS@vT|^j(05Q(MGRz8 zpcV;Xi0?gX7X^d6av7twD@3t#Yw5E!`jF3ULSmPR^6n2GK0qPCUgU;=Cc*v3hrSLA z4Dg^VfKX&;GDM$?(H?0>;I#zUPD3vpRc1e= z8fgl>28Rz=@+pXb>AKl~3nHy)xkD}bGQKFG4FA{KnM}#(&ZqVQXJc(?%FZYoBSiFi z9XdCnJ#9i*ElWJfL&hn>3{=Nefx$=7x8_wYy_Wzr|HD3a8nmU+N>vD4fDtn*L$?ERd1`i}M0@uz(ec-ia)Ig(p~n(9c&m zp7Rx3Hk)KJu8%o4u5Wsi+shpD;8NRW(=zCqT0#QCAr!U~c?cvYY1afJ`sm_uhN}>z z2j4t~cf9G=?1JL}ma=Hmh8Z8jAZ!dXfRD`*T|jGv`Nwj}&vIz8M3kL0(uAK^o2{D~ z$-!*Za*`(SJ?nJF<`!i92e;`8Wsrrcw`atlc_+?f90Ah9S6pv7ePN1>z<@A6R5f3P z7Jhuu?rVv)!nL`w_0#R6v* zxr0%rc!Yrtf|EhdL^111L<<<+Czj1BuOirSg9r<4GGPdjAbeSYpb7ISa8fP$_!$8S z>G$-+&wZ4sogV)>GL4Klp`Y`(Uza{@v0{LTnimijr@>yBB5l*0J~=en?JIStlhq=++e+}WKaDLbmeW9TMvr<>u zx0&<9cu`-FS~L?P!~c_(5@gyiK9EAxOQ-7>;YOzu8q}sWRc;6HiMsPI`Fe1R&{IMV z2K|p{%ft+H89qiGuF7fHrFFB8kwN-N#LvkZF6w9JMX>Fs4pyn43q;!hFPtUO*}T-O zHc>7UGar4zrHn&nj!us+cFa%OAWzyQ+BOAr|TtdTXSdg1nQPNfFZ{qFEk zyg~ukDj^;0L@X#?1rNtLRYpiH*2s|(PkJ5%vnnU2|GI?F53=zTg+ricair-*2XF%_ z0LbMyYy^-7{oX2EKq1-am5bi7uBzizwBM2MUpH|v3dPy?oC-r(`4WD4_UsvukAO;QHQL_J?tJ(@4TtJykklM04uLT4aXH8hL0msDFyP4f z{P{Dy2CO`|_SI|G>YqRV1Oa1gDo=sbHuwNnLP|;?Tx22xG6)VF(m`3-3iOd9!{7v> zZIm%(Zqr1-(IO&ZDE$QBB9Z6!dN*0};X`3>pzHELon;qdmS$43TT@fBY5)U_fhZtw zbgAMU7qIuQT(M$MURU_?<(~I7Kn$EgFfw{-E`)q>xFJD~2%LJirU`Jj;G52HD2NSl z3_>ZOfGhKxjJH}3#BWhe93}xN=_JB&5`@|7y!fKOrinOnnFp1X-M`&mpL>axGpdr- z{OrIPYisLv1hjNwXJGh@_~glDoOuWllQo|`8vs`XG)dgwli)_fU55Mi zIJ$p95HR_v>pSDu*N_+Az8O)da)#c zJx1UU$N&Mep7-$62}k^@-H2#l+zgU0FMbg8fhl|NVPF^lCM&}xT~1;u{&h+UDR4?_ zc2%eLi*VWVPd_YBW@;69*C!Viy{G%n%@+Qg5qM>i9`BH=trFj}=Z}Kq_SI@J8?Oauj)*}-WyGX<-* z(Igmk3(3EVx!RwoeN4clnbGHL-U&UVZBN3(lt9zqaaRAHfklaJJGjNn z-b-uW3aEi&baGk%=A^Q4@pMEOqEi*4os2v`e#t32S*&z>kj6~>0%_gmoycJ!Nn2Q^ zG>4q!n?6WxspXX6g4%^HD$gACfi5)wOt-e^kR zq-6cM8HK;ydjH`=KOR@RAg8~-|7QKm<6ujk%j%l|S6KD415651*4YH_n3R~93@DXL z+uO~1RR=4?G5Lw;+&b8H-@bkOj#gAODtJVcL}=tdM<|X(_sjq~KS< zpm*m0GU-83kU;ZBWb3$l!R~;dP%X0ul>6BHpt1L9^)`y^S@_#63Da8F5X;)@5eTeL z*zp@TY=}(D7jV0sNR|=wx;n#T=-y3~M!?K9$Ydm2%W#-P~_y{b@PRXR7s`P@}2|*gku-uQg4|JL3ha!a08g}X; zg6U=Rmm*4nvw(e1sCMAMDJ)okXB60VGn!Dp0Lali?t-CVl(Z5z5M+a&KHW6Ru5D~A z^Bp$_gvsYojqa4HQ#9a0X3iH0@rrll_9hSdsqTu&bd{a8ztEM~>y$Gv`R0r%qr25s zuV#K72<9wXBcuJpL^=U0DhNpVsBB&x=iYNO@35R4&?Kfn>|a)93n*f5@AiS3d@6u^ zscTgECccRIb+80Q;5il+7CwJIxtaCG za<{lR7AUMe!Ck?d{sna!Kmqf6pe&+l_kErJe1Aol=h6Xqee3!e*rrKZ-sf!%`5NbN z94fX0t*mUKeJU|A5y~4z1ea|ayl`Ip){pE(I-;kjm}!Q94U|OVt@#?;SuEDGw`rp} zQhz`SdX>Q5dEGx#gA0M-GhGH?0)DMB=9uOq*px$%N)?$a5;KN{tlCVi{F*E2^q;du&=vKdX|M&H}crB7> zUj&T3hkbDyvNyQ?fY|W1!3mM)f4X9PUwHg{BlDND^E-dx!s22WLPGdX>{vZnh!X_+ z=N3}i=uocskWL8g+x05D=q1#0{t4DxbNwSGB9e2|6^@1yDBEh5VJ%b|2z3xFy zWv2*%9w#=ZpuGu12g#+SAf=k&S+Y<9;LWt*Rncd2hZ6t?z9|TT>oC(^AE(wZfaT*y zLuW)+`_Q59nKFW200np%rClDpI4)&0O=Pi|T zWXPZ6gz$b%sM)8Vfx4lU9|R%55z}Nv%?oZ&QsT1qb)LeZpmn>3vDFz~R=ima|6hVtegpC|um`gYgUb~;II?2Av$ zYumXv&tEgKr%wC0)z|6eIV{e!B{U2WABX$uPR+x6q--&wOO|5zg6zcS-waJX)Cn^8 zR|}K-5&vt?I+5uQe|8V;B56@E1d28xqKJaK!+R%a%RX-#wL&f@IeGxIo%McbmKr3y zyFl=xRrMo%qv2kBcxV^Gi6}DcleN&(Bodk`QmoL@P6g>m%&(P~k(7DU1l(M??9~eN;!~3!No> zVO{6p=cYDXAkHnYV==T6OZ4;S&%GKci&G_I%>Mp^Lm2;|U0E4Wz=s=Dv{37>u-sOY z0i9;Bw+HpMv%BiLK}ASg5Y)ho-Q6s+n|m4%cozXI{{&Oe9IN*df)M< zf}lbuU=j$TAczkF!w*g@d;R*gB7;E}&KH8&s#datU{P9HI%a@5iD2yP?M3Md#7ThI z95?{(WJ*OG?6pH3C?Q@pGdo-V`P&S7O{qCn`&~vxlqarWi;s&$QAZh&s2^!i z?fTM2LnS$7$WcRq|9(Yf~dP9N~+ zqY?hBeN{y8OpTn#qyVwr{Je>QL9_XbIZp!$3rsfnX9~0?oD19S!i5#!oM0N196aCs z{rlPOm(QN6f1_ipnBKylC6tT>sSZcgzaEz zFHF6PAQIt(n;gSRN=nLJpKyp(x_-n)Y{}8c%j*BJ+03Q7j6(~wiURR~ZYan?NW=b> zO1U7C70z_?pYL``P0>1V;A>CM^{4_M4`jzWxPlk``4=$kN2l<5=)wY^3j%;I8FasOMzH9a=Em%}T5d4QZ=X#oWCOB+bc59dlrO_Pg&z)CpbcmZy%Q36ZKS~kxUdvpWES0d z05yi0cjs!~BcQE&DBg=N3I0h%3~K7_q96@YiD#4Bd!f^cJBL!094M; z%!tU2z_Dor3P)0ibF&>5u>>oAs)W@Rdk|eA;d$?g#3o2I4=<0-A-K6^Cia1@bbHPi z&`JXrIN+@2b(XEQa%^}#3Zh^j0miZgx$VSFnk}Yd zR4m;Wh0cM62~OtfhNt^9YRk**;d(-BD^fkzP}#I`qZtAWOz->duisHhCKE-$FlRBoH9}QRJPLsAiRl$Kzw@3{b%uXO@Gko)IEa=;{;9 zk`Rcop~9)OE{2YXk~y!wap6LfzOA$`c#4oV4C-VsujZ<)co12IbS)sD%}`J6aDTw` zucw6($|nTD*$bkS=p1P;WGvH;-|`7R->q!J%qM+Xm^H=B#T_5}G8YnSdP|V_dEXyG zc+TdTO?CqVPJ%hs;>`X}Wa3 zD_5wzeqpX6MD&ngup_UZ!sX+>5U+N(=lbL9B@z<(E#Khp~RRF`>ojVb7@2Tcouu zVQ!(pZh)wqj2k>N-KCrcE{GVyF*wc$QG3hH>J7pp$s6^lD+@~}bPWB1n4beV6f{II zakg6^m?Real9W3Qy>Nm!i`jI`dqSJj(#$IOR(AM`S| z&Ub!ZSXfw4>{!A@VnN?rtC;;p&^*e#p2i@EJC!>2XKRUwqU+7YPBI8>yr1|p zrYy>r37{Kv)en~WkNN@QjVj9|km2JfNk(jA>ssQ}u3K6C&ZGn+}$;Nu@MQwFiU+j|7C%5Z7jpFz>1 zZE1k*UErm^k+?*p&v2h0pd$HDOlInuhbdYx2Uc6dPh%W?@Sq*!RyX??$$^b^bqacV zLNUTZ2*hK6uR&Z)Br=DuU_ift%N=t%P`LS)z##O6BqNYXcH*sFv&Jd{`TlrE1cPBg z<&iTHL_kMG=_|CtS?Hp7=rp4)2tY@nXS641DB8l7An?^P2}2;#b8L>D8Oz8?*7m&^ zavqDt&MrXT!Mosy2u2_HZY5y^T}7*0?s6O`^97J+(Bkzr~f%?c;ou@qjL+8%J9G)1L}8;&zb0M8~s>RTzoMt zZw(Ii0nbFh=hrj4Y>4Os2W|jRY|{}!du1oolhF&hwsC>Six&b&+M0&a)m35;rQd8p zG{*JubFc*cx&}xRA86cWBSv{1m{2OWxgJuPlZ%Z$^GRArei{n5xDdLVz^s$AMXNKx z1z=*(>fY%{`s}vYF2ct;E-MqBzhJxh*m%`q?RtGgJlF|d9mH+U^nQL|j7d5fUJIpZmV3*y1T_Y0)QCp~y23q^544=GIRDBFUF1XFgUY%u{Fecs`8eXK>#_}A^P zijsP6y(cM&Z!r*n85h}lojglg-FsKYcO<}VFrjKUWkl@^{}Vn@tihU9#0XpKbW$dH z$n$2#5Z_X~bXF#mF)~dV)N$>OQ@S+Wl8u+{m+A5>|18BcjCpgbgGmH@QJd!ePg1 zln{s=ue-riF-=qz5hXNSfoNGNvQsu6t0`4tG(lgJ#+Rt@eQQOEI0CTd6@;FG+f+md z5V{|Go3kL(ro@~``0?Qeg#I}cVeydmyG&d1&zZ&jg{MkEm(=Tev@GNdi8n z2)>}DalzO)+SB*)wX~{sO61^$yG{@NpiAcG^JoBYp@n!Q^H6!zTs9O5AX-GDDN9D} z+S~M_Dk@zg(2od*s9^|D3f1}Sw{PDrwIyBxn=GxB(9qf0dGqnKdTP|#cpCf8a%cb+ zHS0d{_lPiglBGZ&tFV2oQ^WFEMT2O{aj>j+$Cb8I(B!4{{B8Z3{n%YKGo`BQn$AA# zcXV>9!;md6K*DE{BP1bQ(yJ{`rF7;2;ehZN^m%^9L{>o}DgzC2A8wS_*E7KgXay~N zNPTn2RD@sC3{%}9>>CxK0`c7)qPnjr(B5)j^U0#Nqp&!%wSP7=7~5XjAO_@^6F)wJ z=-!LS-3Sy5TsLR{qJ>7IZ>!e!S@J<&aHjLRN1TTfM3Mj~KBN5r`?+S-s%l$EB`+KC zERCALjwVG-^0~8bDJRY-v3E#GksWHbkFIG=iH>i--f_ac_g&J@m;nk1V@*L|X*nI6 z=??M}JTHLY07(H)6!qPZKCF;adN8;R5Mhu=9*58|c&do;W>7Q}xd3fhj#Us(*G{Ls zc~c6>cf75$piXYPjEoA?AfQch9IdK)IR^DYjA|qcV6BTZWoR~92{4ttkj}>4Gmcju zh0_Di|FP8l@4x@G7Z}!W-LnS=Rh(&46O$PCPao~8;)QT+A!_kTPtY2NbMYE!K2XOP zha|tl5ntA?!ro7QvKxUKLK%F10N+bbCz-@CM`i^3G;Y0sk6Y#e%Z^#Lm_16E8N2{s zO>o-wsq^R0k0mj86Fx?2$*o+uGUfH_@0jApLppZv5vT&yeKZy*tlf2L*_#}k%OZiM~zDZ(I63BA6af8@9)C;fk!9vf0 z#gAu!O9uu_dQd&RxdCO~V9DKS=HXar+Yj$gt3h6L}W4{^y!ZXZe7A zTIM=DPNWTsBLD|s1$fIb2JH3RMF@hf3z&ri9EC%N9H6ancv|!^WDX7B=dpGIY%VWS zs5An>8JHF!l-4)F!%I09xbue`6f$J7+l>PN#U>5^1MSE0>B~@ zo0u1jKu0=hG{|}QO;nwB7Q$MnzRObi$4>e1p{y*6J3b&}X@9#kr z3`E7GRX{`uX=#%V6$NRehZGow4iiyAx28pI*I<0T_xk$}-t*kY zt5fHkeRiz9KCzU&?N4P9f=!^OLOW+W6$dRSa$K(tV#}i026v#P z@`vyaMm}=9c#~mUy~rV_Orv!ni^0(v*fJZ=QM@KW!T^%8ZH1{rtwEVhUe8__I2x|o z0Ivczu@0-d$)+cb0HKf?(0KDV79jX-9SH9AhmQe+7O*@`4k@dW#~2JIu4LNp_3OT| zG33_SO#+o>aS@*T>Xn3#rdw&ED_z!5o(*STwwMFchfxqAcgt_u4uSLeZ6~PR-lFA_ z@|>L3;oBDvO%3{lByCMsAccaMB0+A?k^pM-b->*GJQkRF>oX-5(@S`h^wsrsTZc_A z=<>x*`2!re>FE}bsOysD22g-d@y=!g#y)Pmcw$@Y?Afyf92wro>v5>gaJFe%{;bit zU4-5G2C@8iu}fQiodU}oNm*&DXC;*gI0Mv+l->^P36Ba51t|)fq5>chmAmg|zLCv% zRHwp36vgU*Sx~Jk6{399rq_VOoqeDQ$_@6%3_cL5qjcD^(tQv!#eRDbh(k2J=5EX< z9Sb;LB?=pjT{&6Sw3-8PJ5V7ZAEwW%fxH7++|b*KiQ_>#$|Jz=!x#j~(;@T*?rblS z${~eeTtKUjtmx?#&adU=(SQY1SDQe*2ZPaX&m)`S$8fb(T1ZKt8dr)a0ig2e(IWy> z6doTxm0-q&%&26yS_r6fKm80g_~_x9P(WTGw`1Q13(uVzQ--Q(zwA9EB$^NeTeOXr zX|>uOF|^Nh+y)s-Utb@57&Zhja{2fjM|`!*6Rn#g;Q;}QfCWZ~I70c4T=y-m zC+bd4g##lOdkY}lvYV8YnUS%pbB++u7N6rnrLPYJ`+Q%h7Yn{^YrEv*w5v_tf=>v%qD(yJT!z zieg%u^Nk%Gv{AdJTLf>YEd z4pTbuPCF=XNmW*k=w5%KdyS*)xy|@?RYpEcVnr?}7t9t*Qj+pOI_Ztwu5?Fed=EQY zl)FN8MTiywv0qW#eCqCA&0z@4jp%ig>Ib)+O0e@#bw0CesL8A3=@kMSE2k9JY~b|T zK!v)jEZkzS2Cz|*+kzp#;O6EA6|R9HV+AR$_wO$i4FZr2nX*GRAFV>HCY%V217b8%&Vz7R)TIW>6BCKLsc96@Nn9CzWu1wZPpg!Giy*l!ThfxyPV z4wMu?xaTrUT+Bs+^fuoFbQtD>k>WDvaM`ZTfi#imR|E<_fRtKp%%j>u`90O|g^Oxr zJeiLo$BCutP^;?5`;rIZa&ueBX7$cT@yJQb$mqu+Dxtukv>v-$=uvf50Z{Y!4UXaM zD2AmvB}qoczI?+O_+3(%7e*E#3#92xOk__S4Khq9+-T9se5G>7{5{U~+ji;t#3&8y z=w6ioNmZB^!0BR)`gaSLAzEuy>uyYI}tK$Ii=Q^j6yh6$XKL z1oRn)>GXnI+dJux?*r*8ud0cq#^Mi23&n8>HFU&V9Yj?`0&>v}h2Mj0PA?c%VQmGM z<(caPy3*ir0;yBHJ5=(JCJ1=C(CLkl39bsCe#FxsMSK%`LUe)t`w8@7n4%&L!^VJR zFWFL{AW^oZXBkO9xsIm?To}Y?6a!M!H@TqvY3#ReIx3&?U~&R+<%uQUw(o%m4t{=R zs-sp4{sz%p5?7x;>WILp#p>xQDFvC{)_WJhr+>u9>d>5>6_VhySQ|9 z_4T?8Pzt&}M6FjZ&3W?DpEF(g4BbXRx`r46w16RNS;aY4bfY zzY893I`e`d#f~;)bg?etNT>RWJWeS8l?0bC@*)qwH3#MVIxEj&f6#WX+HsL9sGhk; zA}HfV{?(rpm*TtZRm1&;+gMsxvUMx4+{)GexqVPl*$~Nj+B9ab3oIVkdhDW~|AzhQ%YTraJCA?2Xo*3Rzj zYILy{!+vmIo5^>TUh}Gal+0L4?fk7;aHG63KW=l_TXgQ@Mdp$9n|mx>zHum*f;=Q^ zWDD4&+e_iS8wQXEX(n7GA?$cng5vRr+29ruS_dteLvX(AStgOYI5nmNUn<@TRZhiM z%jgK_$7wLv1T4bNbeJG(S<`^+B`Up=M_F(vkkKBy7&&Nfl!<4q?!CM`^Fs_` z&dO~Vmi%pOuP}1Iq*_dVzDnYe*XSo*yYFgg^|rU4KZ>l~YjfTsFaIJLO|P zaPVCTI6iELA;U1dFqH~0#w0BAT6X<=xch)=0pVV(3*9N#&4+-+&{I-MZIa#Jww{Zb zAxS%BZM7xF_)w5{#%B9wQ(fB9tM$ig`J~%L{meb0MU(W*!(Q+d#5Q&w#s@bh`rY=L zaxdduX6q{}?SZ{UfMY-ju-K(#8}oB4K;PjlYr@o=#9{R0gr+>IVCx9rq&?Td{Cs@9 zbkK*3=g*&KXRoWT=U`#+wV6TH)j<4~9|!yd^|f-;B&gpa$qN0qvawh}8FOZjNK`p2 zJ1|D1jF`8Nq4w9xudn>b;M%$w@{{1J#cc33t>k*Ic#h}|2_U6LkA=-&dH$_3wty=s zQFhO1xueL$yZ7!%9Y}-|sB+^zaE4#@>lqpunJ5h75>-%>Sf-7 zgmiy@KZI4y^-o6Vj~wv@xbo}U=&SwFL6LM}6qXF!UTb4Vx4(N1CMVsd>^t0y< z_-u=;Ly^CZuecJ%Jiltj(0S$cxcFpgb8+QGavPO7`H`A{p7+2U9UnLLnr18lv2`}J zoYGrga{lnzb6F2qa4Z<&nHm@2g-7|3gw?n2>KlM~ITo-KQQhXj1$I1Gf6~k#3u|j@ zP#wvulYy2t85^gdn zdQC*$^4pLSl9iSYS5QUXxq`XeprIGXR~Mgu&g0E@Ng{$q1)__8{y~4rwS0>y-$T8u zy|OC=&&dQ6BJox8d%;NVp^Vca+OP?rz~8%jcQoQ2mvy3mnZb2Zzjjcp@YtSa7$00- zYi(7cw7@)>ieIn8mF%c>XWhs?Sr-~gWD1%cmgEeTR1De^G%Ejm!NiR@^&r&FG(YWk z!Xw^RFfNftr6!-p1}zuLq)vbA#0lwSNesj56MorX7UbU=SchfNuW59oAT?&FF|`SG z{dzUb=uYXJ%D?Ym=Z5Gdw}BDN=}W4rX_}BHEtJBT21tW1OCq#tm%Lyxs!_;#64aV- zcrC|NwhKv3hJ}S?f0hf*Z7dkCH5Xhp#Mln5(EOC93lwW6);4R`tM5~wJ_y3IK4+xD z=%`ENJGE&#oJS!%83SopoP`PE-k~{0EC{mODnBMHF%ZvZn=w|PcH{2iBo&T|A|D}% zkn&R0G6zPr?B6vI(QY%F@&5e==v+J4D>hkXsI3Hm=TxoCMK7p=Bowlxb$RH zRq+r$Cdfjc;yH(kFX0i@n=GkIV40#Q`YefO$9embr2{rJputwrT{EGm=SX8lTI^W1j(0A=j z@_+uZt-Z*>$~sx>D&-i9a*!SKdk6BTDOgt_w!tScDauM2wt*%FI=G36iN4YWshRk6 z*|N?toGmW-TwX=>O4I?VFzA01+0piJvKw^F`#hI*L@3?7N3Me<+E~gmx10Ocuav##?O$vqG|PGtpFbr z&cB4K+`!&G-#*yC=XiqcHL35+2U*$Pd|uC%OMGV29xY&-KlxUU|L>3OuIoouRu&C# zobXKZXHR-buGdTu(=*x+n}%}D6109;tg6A=cN16WWE?;rt!GaaIw?)kGj{j>{Cz8- zsZ8sL>oN|@iyS-3-6v1H3);Hm)rjW_bAu`gbXUrqL(oNw>I4^O9?xZzxLkgMB=vd0 z99x)xzu0j(x^?+7vW0_kk~WChn%pS4BEnQrRdQG_+yKvf2*nu9^J(y^rDcYIE@s%3 z>uSC}$KUTn-gPXH3!aj50Zj7?@~E+Dzft*Vtzgacuyls&(`n7Drw&zKs3brg6g z4=L@2X@u|>t=mF9^|7gQdLM!KNY{Q!VFui2RzRr#)E*RTkWGM{i~;vGYD$QlfH^;Z zMU{~t_6|<0H2{U|SrL^iWn2MJRt_#ORyMJGveApm_U9>UN^l!u$7E)5ttO{l3@y0Q zX&tt!%1m*HR`P*a8j@Q++&1$o>hpgmw7K%_j|;(DOxCv|&3)Nfq$UX-2_PhoRHnd> zp%eu-*4dTx`dr{P%GWDcMC%N}Kyy@Lp0BWO2UblPo^U ze#D6Lf2x3&3R@U^2$JZ^hyg+8M?B4XuLR!LvohpKdwhntQvdYnzYH3&twX6L(`REM zmz9w2fg3GC%?VjDZ-H^0mS%qG^%f`XBhaX?D$ffX97j zqfC8ChgCls#~!?FW`~?ejg9@38E3B*$TPNsJp60fm~(x<$@^OIsC`P&J*vLYP35(E z7;9sAKW`3?xj{?lnKx`HnkxrGvC|shMFKmZjlj{m2tGyTkC~{h&;^5TwU8g)4k0A{ z9YTo>%_WanpS0m{egFvs1}5a~sw@l>+?x)gfTX{Y*kcZ*ia;hB85ySaUc9&_?a91y zu~L^dmRT*kBi<~LAn|38+G6p^X+#$gbC392x)=z_G7B~sM4JZd1kK}j@9eApx$@1G zLyWsp=qjbD+Mr{w9f-1)Qpb2i%7(D;=LoSoFy}|s8gQ5Qgg%IRZcQ;9Ixq#^FphJM z1&g#hLsgshk>NRehXX!!$)Rw~zP=9mAl?YHQ?s$)f@o%n8pgc2*32!Y0T~ zka_}2or^D3=T_Ka@nVj2QLLKKTub@#$VxYWr`y=XB&R>&|5~H1TqBPw=Dx^B8A?5< z))ulJXBivAHONC0R`)I_=)}Hp)Z>f!Vww`omE8;2QZKOXxoS5oczK5~c1Y$qGyiuZ zKW4K%l&KsHvo&WnhDTj!2P|MNYSf5qRHKuLV)i;k8AOR6CY=2uhYu~|`MD6&%q|1h&rJwkyB{omOhRsgYxEo^#FaDcp){ES4~8C%@P zgoWFccetqVU}(5+iZQu~y4NP`V&s z!Bm@x;@`h^l@`UA9id(4XhKTM+)P|;^|i;RBqb*|aoRhC`_=HBmOIN`Nf7IsDlngK za7-D@$c~N|NKizLpWtouqS`MlBR_Jnt&H;T!|X}P;ti=b|8v&(gG@#Dp%b8uD>WwP zQ&Ne?cKe^Py>o?FxN>l?&aQGC3`z9AM-HO#+Xl;RW7+pJ5A5`U&msMx*XMJtz77p# zG3l#B7j&mV<;5_pAeRCaQ!NRWF^aw2gx!ZZDT>Ddh13G-@&m^D@q@P>^Tq2G@VsC2 zzF=lErg7W&tr>6kTm71I|HpKYTY|cSq#Sd@eUUG3htY=X(BEmxaJ;_51?yCyd}2*U zT)eK_v|n4AfVB(j*sG@L4;Gj0e;B~@)y>wR-MqWNhzZ=En$;fZ#nnZy_$1tnZ8DX9 zbwgq}v1xNqvr;HhWKA*#;I}>pnf~@n#2#LM3OI{ghtFFSF^RmJ!y87S-5dEH>T1 z5l`js<-i><=p>l@LAW7bkoR)4Qb6q$t-XJHey$sATS4BeGTVVC8ZOmGcuHd$0(j&x z4b2@~`VhZ8ZYsgoCuZ9=C`Y>da7dvxqnBi0;O15o?+fjSA|Gt)-$Q*fdm@ju z<)SU!m?u$fWX#cLCY>_FXD{Vd7h5R?3OGL^Z)++G)xQ+oA>CxNG)Q;Rx?p-ojri|N z*(RQ`FmLQ(uHCMAT~A|*5xFZk6CiLgaM3SRrY0osQ?>JgnN~Txv1#Gl)BSVnb9#zo z-z;ig)*4kpJ{7!=yEciJcckgAfA(cL8vu3GXW_) zb1J77*>SU$F{)4hZ4N@4^ai>rtfkkObK2k5fYj1}^m;>0L4WNQzuZ#I7XMCn7$pxS z7ze1BljD#DY-|*fV`_#Juf;tFh`~MdVpYWXu0>yT$Of;Ps<93`x#!;+UTmwktCwOj zhswx3M0Y!6+I;4euA%6|4H?4D0%3cGf#Lct;RU7Bj^&$I9TZ;MTlmV8YkQY*yAMgN zOVLkE-4rZx3#$;3<1$z1lyyk=&-Jev+q-CR-Z^E@q7sXlLI1zoc;_NY;t2OP7(JiT z^|j~Tb_hCA$~_!ArYl4A^qkp8qw{YkR;nW9C}yO-WB9LlV0yaZ^H@TUo*ezM z*C9E8uWj(u-%q+tw_!tdVXO~F$98a!jf#-h#AMkU!6|lb=1v~78EX#uon?ghcpp;Q zFrQ7blM_AgoWb!V|A{f3Lhqwie6+ScE8SsU+iEO+`Qc=Zuq-D0#sfn46i z#xVB4Uj`5akWWektfr}#wQPFD5=D>>lD!W9I~7>J?5fWgdJx*y8DU>a@jbMbwMe0= zhtp3XTm?Vcc+Yi;mSJO(c11)b%<})}j=fteU19`lAm|tkTES@AVMtn!+X+P;?8?{^ zrw}@@fXbAsrYIgRx{GJT@%gKEcsa%`vXr0v^tXtee%s<(x({2E_VPPd(>F`=JHf2* zEh0^fZ&c5HJwB!Et8puo0=aiPN)8_$xLLX z#EP}adAnVEY-q=T%I@JV;cCwwgKDYHi)h_7ImdDlX)G?C&sp}Und9GkDC}bCQF&uW z`>u{aU;^ZKS77FGa&k74AWGj9WScOQ6|z*CclGM=Pm6JNat4w3+q(x$CoK5 zI3P8nYK%lS8U&pv`};{P8bQ|rO-=oVsAqAkX=n%(W!KznEf~+8tD>fXG94WVgm6tT zI?FjOm1)ytCd8|$kh5w&Mc;Ur%#(e8e~jy`(VC(^>3S#X_#P#^R!w$5KcqX&XyuqVsFx+Bu_^93NQ z4Z=#eDA=}l`z%+~)_T2o0d%dT^z>jaNJ-9X3U(*J=Rtya;eA?KUB(pv&?kE!Ijshi zEYMv7Yzi9zTAAn1o>fxQfZlTQtwd~gZZ60HL4eD|z!0}?yBjf!ASE~t{x}+x5BD7m zKyaKpX8;W0&&A&1;c6JE79gKXc+((*Xu$acgjzYJFOD!gw-JZHddwQ+Tr!nFW)Q!5 zQ=mAu)&Tq`6B6`5sGh#75FmO`Iou~lf)pFMx;qdNYJY8po8E#S_?W6mHi;+EXvLf zI8B5oPDS`Q{JMU`slvm3OPsxorA_u!w2%aEl$4sCpC5ns9^@PWV^VIm!0bwMf|jXK zhVlZyT16_Sw|_=(fc;Gt{{hhcC`e0>OFd)Nz}}UsA+iQiTTr$Fff6JzeT$gj>x5R7 zQFXUHI1BlGHqLje-sU}pmFZ-Nhe*PjwsW{?UUh3L@3M9S@5h9gW+1l$Q`;8Q6E+jA zV`b1D@Cc;)A+Iav0X3NecR0vc!+N^zknnJmyiNd69zS_vW7h#785IrTP``JNNfby4 z*lIvxu3x*hi>a#0J4T|>l+y0r={}9SjX-4?S8#>lEO%+sM>BZFNF$?$Dd3r?YF>|f^X7E< zmumC8_3z(5fOjkw8zp2b&o3V>ssh-<|5WEaXg}{ea+m#PvOT-hHV&MH} zVo&R0+lBx$Iy7`PxP%ZceQACD1aqJfv{c->E?rv#jAVBE_*=JbX?yoKAr!d5Ct5=3 z?p>o*U{JHAaQlW4^?)cV`ytq!wMt#N;%45K;RaBARw0WVeG?9c(gk-`An$`<8Y35; zII|bwCL{Fw{uX!dDilpjW}y-KN;bUf?eFU<7T67i42!%< zxiMc*H188EyMZ#PF9R7oeYp=XV~2lkmc{O$g;_P0oVmms9`cYK4|lkzGj>2`boOHu zsU4`x;Q0U@X7Ap;vmiMN2G2A9U4k`rbGyMJ)0S+f8dbTry---jRBwsaeiOGmV3@yJ zg^7PC3G8@o@S~6R5vaF^3}EmTpr>AXdZwhOkCg90e8>i)+#g42(axypq_QJ86b@a7 z0<&yvwU!_5L(dy{T?{|w=;#P6{g49N<9%Z^f!)B|)6MGy_=rZ?PZ8uaj;H!S!)DQ$ zTbt<}>9N}mG+&KuFm-y11|iY`KWCoN2O@T60f`;ZT2C0dx^8Rkv4imq1kahp93bs| z_aH*BPyMBx}qW@i}b5clwTsyM28E*;E{s+ zR#!95==vd%gYE7eZAB-dA z-1yOx_sdt#T1Xs9KSV5SzXX6hsD%Z}>_AzuQH4MRqkmu0=<%Irxrg9YO0}AuoqfM* zLR*OTia`v}MJ`=x@camb^O*V`oBeY9{CNe%>7e?T>Ib0ivS@)`rC-!;JMhnFeYOY_ z2s}gD2|3t^3A4f%za6Eb!h@+Z_N6BOqL~|}-82`(GXU%wuU!ViOYrk^G5Tk3r-9x+ z+_OvAy1c#pzKlKid)QaCwdJiV_huvg1AxGpY2J(gm&^)Qbx4ps(ya<$0f)3ZnMFm; zz&TpGyFwIIJO?9hoG|yaB*O}B2dlQAx7lEYFF^$T@Zl_g%U}UmD{z6gv`N++*0$l( zfHt(km#P%OC?x2U2-z%(@)N{h!0#AeZf?H2dK#cz-Qh4elfx_+ux>Q`^J(vlToF<` zKu)MIUcey)R`}e~lE!=ON5kUN)6<{{y7L`0Zdhl~;=r5@lOvN z1@uDW-5_5{RH%&*SO88dkhL7_AP@vx_6~hyr6NhJy#wJGU!Rh4!}z1C3M1hou-seP z*znTwl!8xtV`JlIL0zD|QTZUEWI|5dn})Yb7)N^L%tzB}heV8p6BH zEsTxBxb&;uvp=-9CJ>H30nsjK7sN_yDU{VJA>4-E`d3A{I~8F{FLv2ngPslL7u4S> z5XjAC+6Hdm$pZ@|Tn+NX^5SAja?GO|pnu9Xl@}f!jebQ3XD7?9h(7>2aHht| zWotcHnC9l@{yO5oZ-=oEXe#6HYQR_QtUk;(Yzn{?BzUP%_yC2p5^BFnLl>tNu=>Ik zO(1B)5Tn)N|)B-2{E6XI~Ym>qy+;oEqNa^WV~VdW;lBm8WOcPQn0zLE&SQV3m3qVR(~H3v}?wd zdw6rR<56ga&a9`tFsDI0-pbPmmg3Ri6UtAzM;es}^z&*R;1{do2_*=2jyKE@?A-v1 zT|n%B-E#Z>eIrWZkr%ZF-~&o3fRNwVIRH@~N&+WIDS1BNX34LZnSu_!x&V6S?c2V(WUB{Z z?T6!a0uh_VJMg7oWAo}F|HqoZ{|Ir2sZ~_$t<4cd{Kw@8u6{83~hA^?Zx~frs8HqB>>{@9GC8&CK#~y;aoT- zYXxL3KP3rX9*!7dCX}Ql93*>oQf2CR$J({*&+%7{E*U%HAGk36co(7Fcb%|}E=?%1 zy|hfuN&f3+-)Nor@v}V#xQq`-x;1E@oaq1A7fKQCgrtR=j3bBkcUqmpgoGI3xZ?Wj zw@%h5w99WiAt@MUHXX_PeJNbOF6HBwikvTEM(<5@231`y{`fXR`}FUxzutU9dVNFb z=NqM0p8q=E>F-x}eq-mI&4WLiOY_R?lO49l&dY1mJE&dZDZzs^qd z@z7bq&Fvhc7CYbkkiD|&G5w95Tod8-hTg*jF4aTi%%0t1F9@gKIo_1~ZkD|+;LQ5- zUa&k8C9uk6bnNa!ru8O zTjNX?c#SE@N1h$-_PCR5oghE1X69YL*0sM?+S)_a!hU%(L_z-KGy3Wh9!@K^-`%IQ z6lSg;18THQR{!=la_5+`+~MOa>$Qd}BP2PUokJb-WkNS>dxc9KL%v!7&3)$})BDwT zAG_4vB_GmgIEjy@9=zXYZ0~m>gfg!qqx9RCNK&hky=)4z7xykn&bbKbc&$!Oeqf_d zpj|sZ)$p#FIFioIu}YL>_UZ;-wPHAzyptx1E#+)w$)(Ar@bc-YgILVu!o|G@*<{+I zsd+g1v!)L?#@o@?mpCN|bH2T7rHt;2udoT)WnQ^1DlK%k{nnY|xaL;V;sX19(ar(r zQ7e_g*7oO%x_7RO;M5~NDCX99cgE(Ln18Pj-?wq@W?E;MC091<$Mn;v?t{J47_p_Q z{g)L{o3H%drcyF%#EAu6b@^uCp*xc*|9gb@o%yr5;Mw2N8vPvK?Y$?l%!%p_)x|KX ztdCB{wrFh`$K)X+C}whT$V2s9w(!+xtD2WRua=t)b(3+mzOHg+wI@cPo#8}|TdliZ zed?sWkd>5b5A$RBr)vL|9%Ne9F>^DE^;pI#*5U6iwLUEA#o5V*OBGA^{F1Hps)cUT zBQwlGF|oe~W!73w2Yo=jWd?=epcWXlUT zW)odKs6T&Ke`*|c6<+?*aOEE_dH?ybUJZ4na_=duN*V$ zzx2qr%^7={)vC7ht_@&=NZ7Pm?S!&f`^A^oxRX0WZB+Mbo%ixc!>Y9DG(Ca;Q)<{O zsNT#Zqw$2-^pYEB2h0s6iD;eMo!v0UkE;bnX@*<4@C<(t&l_|$xW}K%+xs>@P)o~z z^0gt!KW}#L@2MJwd;WbYL;J7jf=zjAjc<`DQ=vqW)3FBzU})8P@CP%^-1a|^kdZmJ z5Nm?9G_Jv@+QG^s7sDR-wYm)N&6?W%0@3^O{*?W~TN0(!CH?M_xY?J-SUT`M25FUf zYzqN&9`aP7b|cC4b?K+?9#DX}r^zliJ1=Z2sU<(NsJLbL`?zS{6}XVKqzu1Nu0Q9%IJ;mVAL1YRF4yPl-#_*ufoP(@8wX_Td z6;_kNsrLr`B+e2BZf(heJ8epse88Ce*D~9;_4ML2%R0rh9y+H?O;K$@gE=jND(it1 zE-lRa42miDg7dpKF@+#3H?Mii_S#D@x29zFU2&{qRk6UFn%q>SKwP~;yq&EzqsyL{ zVP7rwS6Wepc};%96wX=!H&iq&jK)$)SH|tPz5f5A`h7Ws3tb`uo1-RxPK~i_17pK;U6extV_z`gm?@;&L5%C)w=s zP(+~D6uN-@JtjGQ>`UN-<5D6i{Oa zT3~wJ5%rdJcMkIMdUg8sV}{zgW-W!?IHY4)3l`t~^@6@D83+aNj>{j{Yze6~mt|Ph z%*N!CsbNBhdf?%YTc2Jzz%5|He}~;#06RUGf4pn+W4&jmx@qzxs<(AH*9S)~l_An$ z=E^D`=W+>q!!)KP<&Nqy16|IVUvxogcY@2nDYS7m@}&pVDr zF6FADEz2-Ay!W$s1(GgMh)z@zu8c;VLPIb4z3f4}d0w^Wn~5g!iL#J)i{_e^&iH;J zioX0CscrL*ZuWbVzE)H=?MP7mvtR$XOb8CHmKa>fxHp!+#039IEU=^Ri5pfl-!=-{ z9G$m$ms!?1AHY=F^fo1@Xlm*OuNSRAv;bvdzIkVs30zfIn5C6cmw|PGO!FFgJ($!kZY{`^=Q){@pz2f=dBDj? zSfw~iP0TDNL`&5|u_a==glTmR*Cda(X8n&PGp<#qdA-6suGRaCQtQ^nILrnP|TfohSAokf|Hd3x?v|fE}(47eLn}0KBp)(We$B$QEt7hp2j*@v*nLQefnpk7M`3!ieH6FOL8*TN`5V3I^AJ|WQL6+Xpkf9T}=K`UyUk;y*gD^~ig1jfKCS<19 zyc5ezFG1m5R3Q>KD#`P>-=-7J@{*?eN&-Hxdwi2O>tLfyT9Sa6{ zVq!xr!{IToW;`%)R5P-_hH0{4m+7&FE=$RyN^wKR*}UrVgHQ&ZIr$)Vnzvtb^}efU zN=mHTV0)ruG5Nd$gNdF;VT7UeTpp~slt@MH^Z>##QF?vPuUPpZz2ET`2t6IG>eN}i z3T{zlu?&21Y>es-jy{%e-hEK3K3oD*=7b+1<>no3g2`^GMzBb9YF5K1{vymID~0xY zYQsq|=9c1TUj}SKpR}l)g%D^pZK&7#KDP*Zz_VS>|7@F0pl98SZd5&KZy-dZ*%yT# zLX0Ybbf<}yORn_i#$=?fhPVCET^WCO*A;G^t52?Bt0KG2dn0L$M01$eJw;pm$AAq( zxEI2)``43jE8H(2AC8PVb1>CQ#pW53*M2xYaSFX&)x7z8{zZuVUW-nrU-RC7!P|6Z zEdq(O(6Ih&_=c9dJ_%bw6s$;3O6JqtgCh*O7k3%%x|t8 zdj382=LNVuA|vW?{t7zRcK)*J>Q_fkA^<9r5&HK|dHN_JySH{se1Hgk#R|#%ul;z> zW~pBfxRUgI-U!{Pp^~NtaN+EfCVt<)w#&9+m!9NVTqZI=p|Y`@!RHW&uI>MxZ;jUa zyM~rmsICYQwZ7-FPyKr{K4H6tE>#CH7yymEjx!>)vzH+;-{_+S z2FRPBPp)}%J5GEGpOc)BFx2Uc0ptRJSrDAP&C9dZ*4BnhzM5L+rkE$7M?R-GbpR;p zm1Y8uBm+qPL5kSO!eVAs6UyGe7XioW>FI$#Au(Hor$zLMYW?Sq+e#KIXA3~ODIX^! z<9U*bYAv;}9HhSBha*IXgP+{BYuEI`M^A7N1S1dz3GWT1cRKx_p3V0-p3=sHsL=&G&l-pAFobA zW6dl$3Paj!rScotmrak1mR>#9cTY(PP(75Q4P=O+S{2wrLIVJL51DF!0_uEDBAnEM z(f>}9EHU&teb)e2Qp#@7hzkYHt*r3t7Oo4B*UQd8%KGT3Qz4Lwjf|u{c5JY}KSm+{ z!v`K--tOq@RDf>42_RpJDvb2;Q2-ZMNJh)b)_}`%&bUE+l-YC((9t?UX3j^c2YB&VZv|6ckk5275!n2cLFF$}DVo+FfL4Z{`009?<0#1Y>YPf%v?< zmCdB(j}(1?LP~+##0Ur+tFr)JQ?&uv36`LeA^DUL`R@$U=2w1tmpqd;c3MZnL|dwZ zVRew%6(qL+8pTTgL2N-mb#-khH;5{Wnz+Emy7{UBYXArU zbOlLxC{7g_l(E|mIugJ|@ET-fZI_@XrnI~~`JqGg6&3G0OuJ8-8yiEOb^oD5;FFhl zV%pvf^LY|I)6y%6j4L?d|D_V%1Vnp`@k;6RP`KTCQ!ztUxrBlzido$qEvdqj8`P_0COZ z7`sh0=xx6d;a9F!frjS z&|(pPX)!ulmk}6OHtB_6_NkL;*2(wEl^-g(-R0&^-O>h_ZT=ekSW$NyfMD0oRcn9n z^P}*xt~q*lAo}gw^I1-+s$ud+l8#y!yYahh*i}C^gV`HR6EzCcrBu8?d<~5P8rkRmrKzodj zzhllIu>;0a&?lG-126#Z>B)c^jGW6_-I0I-Ws*E_aVa(hVK7#=>;%Bd$q0aZ0rk4C z6w)MV1yBmmAA;t)ciXAnhOUotiUBvziYzFF(d@N`^X6zi2z;MqRj?2=C-7BF0U!=a zaB_592WXIuNYlcE4nfkK;Oz1V{f(ZdomcFE&_4pG30)qIiHkk1+neT^pZxl}^H_Mm z_Lq$V1c9x}QNX&QDj5@}|_>VpU;H@&tq|L?N zzgXOjSw7++YcDvJb`tKQ(=-=oFND?IW$_2J#DBo{Xdm*X&6+biD{J#35A5w-g+NtU z8P~;4nk`RuU~iYz0CJnzq`@QFj7ZPpJ%k`0#{uHQI(LX_5+80I)Vu#@z;>#Fz+4JT zbM{~5237;1Sc536?brCzbt-2yLDX9wO0m9VMtAvP9~~N`Lp~lJ9YDJRLJBO⁡^z z(`esbj0IlU(ifFog%HkM7EL(Q3cMUW0%fG;s)hGxtoa>HiTtl+W$3kW>|3>ZV&R11 zVo|7+Q6D7&L>F#u;f3`MImpTlU4Bz*o(HbLW1Y=j6n%1`TnWk7GdoPdFJ*plF@gt) zgBXI1-k*fPWy#bBSHH+LMkXdW@6LdFE8_@&7U2%7-m>-mv%|^qFQ_dCL6~O4?fa(q z7Q~9Cjl3-oytVXNEWoeMtHRivnJNZVJNTPSx^%^y2zs5=bR|uZv2+4;K1A0ICbG&X zvd7IVB5*ku)S%>_kn<9(B*3^bq@bisI@7v5r3J`=oE!7;mX=eO3w&o)&|?=$3!or~yVbEMmUHLMLFuR@>RZZ%|5xH#FR;eG22@nfwTDab=|~ zVTM3zg^+lCL7p(w(=WqSrp;fVO&yw?>;g2H-!BG8CMx^E4e+vjj6JYGI=%s`a`fkv z_U)S=)K^qdV_J$sfem6}*s*rL%vejqrF z3~EF&*H*|87ea7zziOrPCT+-&4qRiRjXk7;|uqfb1{fDVS!cOI*T|O-r2^gxZ{G!j190B5w zY%i!I-CI5p?j4qI(;qtku%YIRD=2pEF1Q0knOo(8wL^fRyHpDb&_PZu}?tet5I9ohKoXxQU0USQSP zVtHWe461)lv1bMHF=JCxY)rre8@)`CQCQdoMa$=mp#1p~6DV$LH3)#Z!p$2uP^!0q zUm~NR&^86rJXk5+=_xNSzvI3Z8W#NqRGMMIWES8M1$!%CnGhBV6}BJGz3)-}{|I4M z6i$~;=&lJ92q-Xi1cMEfH=x5}f#=h0PAF?bd`iG-5GLEB6cmIqN4q1^gN278jef)7^AMWz+`}B2b1P7H*N#y&Dt0 zQ`Gg41%!F9U-fwT5)X0fDj(YsQz*s9{=ldoqF3)9YagBGKS>HOM?snqMk)@M3CwSK z-NGL`;EAky(SmKFsLcsVO-xOsP#f2$N$`=13_{u1w0y;}rhl85=nUKr<23;(T8^Ft zG5m(6s;aR^uWZdz*~>QLca>-E20tz^Ay8=`0OP5i41k_KI5MKPjJuT5^qvqpYvhOe z8}5f^41EV4Xyh6MsyR)Bm2zZgh)pH)%X}#KL5`1J;Vvz34&ksVp2$VheOClwO)agN4}{$^aDGvb<;t;8h%=zhD#==v zPQ;;k?#b&mLXBc&cf$-SPMxRG(OOVnoBYiriX*%>2q?Y2zA>NKEqV%B3cy5pg_;m% zF8}+MHF-x% zOG`te8_Gr@Z$eri5dwA$Mp;P-9Tu}30f8OR34zvY-ZKnBV)$h^YVz+KkL=_I!F_le zT>tpLuV0+FP~P2liN4ptjr0(=pg&=U%H$11p=Nt7A+HrgUkvtN!`YFiw7>$i!&^1$ z5mdYO!jch&^MdRM#M7{rW9{Y$u2VLT8(>Aaef!^3r6m!A2sAyH@WK!%~--CjGo$h4K=qH%I;YfCU|AjH4VV2DbPQ`1|>PaV}s!#zz5@Bfc z_C{9`{iACF0bdJt)2%nhrm0h0NQs|9IR5zYV-N-_9{=;Q`siSel9*O|ZrgjJ6Zj{8 zvXOvpvLw_oDTd=<9xh z4L7j&wdsD82QNDPgwVk%ekW8BBuC&1GH=wm(YhEi6KFXf455<_ibg`ZqpYlKc^N&v z``2l;wQ&$DfM$W)&|s&`{lURFrk(HD>e2in;{Z47EXjPk5Fu0RCR%Ec1r?EDhJjme zTKvssI~Ae_IiH~+c<)fV&CJdX{5lJerq2H_hr9DC@!p-h@9$5z{iJIBe&J{9|Nb{Z z8*%kFhP(6XCAS^${ePc0b>(-K{KtzwpXvWTL2~&2U&jA$jhz^f}numc3wD*%>2>77)ln#uy5AGSZObH2cayM-M`20aSUf$q+Mr!Ct4Gu0rg3PdfB&olmyS;ipa>10`)%QB zvTgqNLD;^$OHHM&crr^@hN$=J7Z7(K;0+BTKc==6$3K7c%{^-+B_&NwI-(xLe&F{O zzyhR?m+ijx944%8e@Jpevs@(o<2FAgr=+AzPMYp2d_>o$^88x{9n3MPoA4`2N+;xK zez(V&rzAE6{mxA{{`b2I)0Vl)`+W&yNvPWXzgF07x7)7Q%FWQg4uBEV9hm6;_V!6y zT9Hen{jflT&k@15AwPfh(F~2n4)@gvi9ExMKvunHU4&8J!B+udo&i2y$JXOfWez_=N6(6w2 zF?#QG1-4dT(u_uEr<)jz|#-Pf&b%bUm#{;_RiKkKzDOrA?ZgZGC|y83*Uw0 zlT{5Qug<}w{IAQTBhvI)ru|OT13vn-aaL%wxrGH`zCT@h{eO+L_eh_}Euts}*gJbC z_2h}ZG64Al(3uC(VS|w3vnf%A;L-r8+jwUPbYSZb2jprZr|*FIO+*9@9bFVS zVq|p)vNgf>Pg%LGz5VT~I*^c2C=`ecnG{V;OyJoQQsu-^kh>~$xPJY!$Zk7tQdd~- zA*Bc`YGgY;K&tl3moHFID5(?j;2`2T+ZZ{NMJ-?zFFf?!g?8i? z6xlO?ZHjI`xP<_$P(`f_IaKhlD}ktb6~nX*K1o7$vwiU88jUiri93G0WlRmK5Pi_g z9zI&0eA&$b1h<0w4i!DTuFNL(T`c0Evt8PmpN{ z>XH<&n|&kP4yaqc421j21+4eqmr}?{c@F_bQ@Qu}!5WDEnqfx;^m0=$S+>DQK)K9; zkgNMay_&cUAvjj>Jv0F*7g6-17oapt zZjg}37)nLv%$b!!#*k#moP^Aor}um;yU*VH^Bewt-}k$YeHM+${u9~f`(8k5hOj<`-;6)^X?d-vmRnhhIpHmb|Z zMeTnEiv|S-(gsp&ZvT&6-1X<$;nkBKd5S7oNtu%X+KNAj4Bdc~st7f*hNg46x_ny5 zuP<}|92iKnG1R0ttbaeoLm5HLskODS5nxCWI5pam=Tv{FC=9(B*;Cpm;3=*!i5MZU zYu&^Waox}9Wd;6g%43&?BokzdKbMY5xCTQ$tw)*<|;<`;Q;xMd1QGLZx zkl6lXq1lUEKxvcfv!{{k9xv^4p7c=MVB+(xt{36({;Id4&$=*5iANG92dcuED;*KG zikc^(dX*}NrzqY(G&HomeH#OVR(U^NI1>1IhW6gm(%rzfMgx#Mb#sS@pEKMs5P^(I zYegCML3tpGQ(}QgX|DnC8E9@aY3JPW2F;m!jBV#!k`7g`8(veD{2yW@8;C!7E zE5<5bvJ5(8gX-VUPXoyN11S!m!S_y^NFWM`b+Nm7DI*wSfc zeP0KdJ}`!#pFih%ZDyv>#$hyz7(6R1EHoE!7<}Bthnr+FLN($_Ib9O~ub$UlxC3#m z^XCm21w~M|Ln|OUdp3Lj*S=sY`jzD3yuEDWpA-GwSLDGD0}H7^ z(huwgcPq#+AwZRfooyev*jw(z)DAV^mQOuEs?f>&+!c`R6_I=hvLmM!Iehrh{goaR zG=JN`(jLOE89&Rdmhs?>Pqp)n(9IFTw3RVYyZRA(rgqFMP35#Zi~}J@tU$mqrgr$_ zUY;PGr%Sd=RHsU@He3F1QJTRGosOQKn7s(Qi0K?WXxa7+8^KKy!(OOHf9YdRz`t&P zEfUs1bw!nz5kIdOI=(HU9!7zXea%6%3UAo-gWr+hH&|Fa(m}nWu_3@0F+q-a*lI`g zNVg>1Xym>rQDk%NnjbI9Yk_YeG@oiy&5_0 z^UiPods%ie+d0&DHFJMon+CpMMrLN;LzAU~_%#QIECXfKY=d~ACJU&iS1_EPN*rpI zT11}~U{}2;=vt%oFFmJ+!U~uy+EaCFq*7zQU7#}K9im==N-7$Wyf2`FWoemYdEeJJ>$wBEpZ!qxEI07l^o-}g z0pSgpwNh*ZZPqIYnAEdWT-)xCSveK)^&Jjao-7oJoyOruEKm0K?tF!4 zq)8p(bc~O76NM{k0@V~$3q`qRVSW~z0dz$cqBXbWkg)J%1cCTTqW82(66h%oAY|g* zy(n9O2>#N?3S)c`H0jf>cD%I=3?G}A7)+PMz>K7f=deGnV6C`eR8$K8+(WU_@oxd)t`&K2?p&_>zCG*6UK3Z}pp&4Lm9zvw0#^F^)eJx^B0y*iG8Kjpz%iyXoVqX$ywSiWv+nCx zZ9oP|>xrh}PU?SOrTB_Gt6fF)Cd@;kZeR{Y1wh6=vzz-jz9;ts9xpAP`X`Eqp)%a) zwZ$aIlz;#VtC?TDK5G!&?H9vTnBeAS5(%JsS7&EkF$IPasNX{~H0dW_kT;MI9koV8 z<2*{e?IL4x>bkmU*)$&uuMQK2&m+w3Xbl?UN}ripStVm@%gOEM_tF{rdG%>CC53-vEWC-A{D#*WIQ#>LKiS#mqd1$$lW9aB)DaLFZ9yycHK0 zPh5`MsjRQ3<-}}%Ty;OzLE*&Ea5y7|iVC5_Cw;ST0y4iJ7kkm}BA3%tc`k%_GIB7y zm`EY8Qub23MhG@Q5y6=R$K^WpTwL;!rl7ew%UV0gUJx*7NQj9BbrX44 z6Sn!|yxa|J`d`0(VdOw~S2{B^Dsa(bV`GeVYmRytDVu0aXP9-g&Q9)-wB`^J8dS8a z(l=Q>(3*dWPr|A|r1enqtoiH&hN_)N{-n_)f<(6VP)g2=^aR(Pd0uFCE+28txk#m; z^`De#N$Ml7b<5_>PY}GEbO!v{Cms@B;?jN^dBdjr8)#^<#N)?o2EU}-%vk|Qk8@Ro zNOSMILaV%yljp3y{dYWad(9XHts;Dy%-J<(vr~y z0lnYYI=Qb z>%UehkInmmigR$f$l!JiVAm&LamE+I`HPIuqvX~megm1lSK$0(^63C zUdM>d&tXzWFyrnQDaB?ike6xix(mfg8^0Nzy{uy6I5nQuQ>_s_pXWGo?!xY|&`>SQ zXrrlc@$T38i*#)6I=(RlZw#F>qciz_c(qqNy1(8ac>6mjpo(MF$ob zpcczQOcjKsI_b-nXmu4FBXVsfrg^?~?YebD-miK_wPsVF_7kcO#~hl!XV!RxxMdYY3H@5Enh6jZu*7lf>< zKfhe#J!?1XW1bLiRR*mY{P*F*Bi?O~)cpLE7ZxZH&eeJA#3mK&65T6waJX`0D<|I_ zU2ZO(z5>@PYHCyCcRf66s8&cS$>!IZ4613^X>F3dQ{UZMUQt##2liBVI}8EWDg(;A z=P z&C@(KA+GP2TAgLI-r8)(pI=7sxMWJ-eQpr8b>;|EiphxiM6r8>xix!x+Yh3t7RvIy4e%tO(TfreM(qahuFA#=U!L zM=V(%ND@W2b6_k5f%gMmj(67zjwU9`@GMk?M+yp~ctSII17nGl`Llw~)~q z8W_5dQ5@hFc@Y*^(waY;Gh=%`QMaX+FaCUT=Ht~w8c@DCKiS4v{2xq(y@*-9Yd9xn zcH0n3uc?m$1rV2h&ci~7!;UqgZdnX&LjOBYLun3T&K!s}nxh?eqMIQeQk7rXE2?*W zete)g-zV}&%C};gDoDJZO}B3mb?d{Jxt6>u6@x7fZ6QBHge`Q@~oRd#J4;MgRdhLJ^!jAQ$CdBkToq91cZ}j*NSI zDIIa(E1NUMDR>&|{d;dA$`9rjmpkLo*kRjdkO1rsMV4&E=~=C$Q^+PFQT4^mqaf@; z+s-QCEFT${6nognSzcDQVIcIlf^$zeDaW~`+gATcpp?tt%Tu{t{%E)-1UDZ@QzoDX0I|~xkYzgtX9Ff_V-48@J4)4t$^BJUNhuz8U3+<_#Zl5Ncz;w^#&z9yal#cZpKY!s!4G#0y z4fWbEAyiaxfa}xfEDg)60AZt}4ci;$Z2$P9;MLElk-ivaA>++2yQw6l6#SWfXIOVB ztTtncU5m-t#s&zIcu+F8A3mJ*t-EKaH7zE-`)jLXmv>$2DA+W^!~H&%qSr7NXh%gL z6#CQ~AH=^*NHuOuY6%=MMWGR4jhGKl#RS@)iMA6#_KGsTlZJ2WdECk&pRfvHo5+yjgAFu z&GuK65&OnZlGaH{2@5;ev>evhs8|{jvc<3ZtFdWuiMOa$w7JHss~xW*cBP%~i{J5U zBtMe4nylFD7kN>mUsAeI`WIq&Auzp{veoa|sf@g?BZbl5>2+c)%L8J4B7H79DV(dK zIPk+uzqHsLqquPZ>}IC_emwxrDG)v$%PfiBu#2JUEG{SXvh*jDxNrR~r#Yhncrn+0 z;35#EwvS&v{d~Qz)eSDL!|w9suAK6L8N?R(F1|k6KYvkR)ECF9R#%%j=zt*7VK~kg zy1y^PyfyW`_J+;BpAJA)fYY1X-NLH8J~5lLB&XW%KhSn98D=SydV^NFamwJR%+*&i zJ=LpLt#nKMK}vT0av7 z7HSu#qOD3nZ1xqXSdISt#VAl9>%n>EjN%9>rT&mbONtGrg2msS266YVcTl!U4$b3f z<+;z7g4Qq)cfWeybkoFu&z9O36;FL~Ul&Y9i05AHI@vVZ8^dfXk{`hmoK&7^T-%wn zdM$}5%+^3%ZcC!<5Q|pOWi_G3M%dw*bEYtM92G-e2k|ondPn`WQl78+X0BsZBcq=p@?BiA=LOGaF5 z;jfA(ASCg7luKNx%$Cd}>|osJC*B!{LZ?F~?aYU61^)o~W*c z&b(K3b!Zzk5e%7_zA^&I%MY!r(gb-+`tJHi1R6Bw&cp z7lxG-H7v@UR68I85*eiTQNiPauChtVORU2Iy+I+kT=~emWo3#|;h=GhZxkA9;l(-v znPru%y=x?KCJ@%%}adP$r0X9#xD4j>~MQ-tGA zH?eWMS;h0ctABqDM$$HXb^jPMHUNu()$Z$ky`V=!n78+hlMkjR2*&_PBDJicAAw)B zoD8RNdLYXsVN$QWm zj*B1G;*nT+ju7Ulx9BKS)8CyM&fV2QDseT0ueS0(xvwuGg2h(-gqTt@q5hEWi!0sS zvBLO-TvCOq%oVjWecrF9{?u}s4y?8EF*P>@|4#9={2AH&YcVt^Le@$VTw zi43Rfb+M&&eX~QH-E>jJOGKvv`^ESqci|tMWUfegm&u=&FE6Xi>+kvFcjN$o8&`zT z^j=Qn^DVz0!P;F*0wG5sJ^FXx7-2ubJs|$~I}6ziPmQ0Ui+h*C8pE%Bz_W!=&|K&yh`-bNif420is5Evij!5nE9phnKWAyG#tGix!F#g zEbL413A&uW@N!$Saen(izqe-Mud-t)0mWBe?))Qat)(aPrQ-+J?OOL>cIZuLQj^gT z$KEXm+h-3uJ?T$PzR}RtlaPTN8euDT5_uJO7Il%o{rz+D7sO^<>4@#N{M&!~V%O3` z_Mq@3@5g`r0#skFa3X@bZ}UA_zyf|5BV=`i1}b zmb^F=|GgSkw|DHJ(BE~@nAmlA9mIqx!ztwzIKtcil7(`mCf57+x8Jt>|K!_|?7JtQ zp*#)Fiu1x;c1=&$4e}y=<0P+uR5|J@4i1y!{f%fpn8j3D%piSt>5#5;0YNk7;}bH$ z;L*@hvewjtNW#qM8`mg91`_gN+#x`K6qJ<4L`TsAduN1o>6GIWzVVXZK8@bDw7;)U z-URhKvGD5byh;6oFY`T;lK2W}-lA+^XJs_r*8uP>a3e8~vH>ljPM6467)ei@80rBH zVtz0;)%p0tESqYY^4F}O9B2{~6`hh?OY(e(*kp8eK4&$l;V^3G|NhgaYuVa4PX^Jm z($dm`4mMO4QO)^u9#yBRru?n#d$xJKPxtLcdUP=JF9D9S{lsxSm~PaAQ*5&0>D427 zXm8ZWMemVvE0ox#f_dQBUOqu_#MsO=y~5%u*p0Mu`;p#fO>45hEcL=nvndxsC?J z{QP<3$pn$x{RFM&pEW(R-9dUjEF$a?C6I>2P~10GaD#XqyiRPAqK^^bDCC$??TW04 zF&qB&!GGX-Y*G^aE!1y6Pt+xLzKn~@Ja1X^-8!2XhniHA4Xsb7%{nJvfbuNi6lE+3 z<-UpMRhw8vpKmAv68}tR)2FTx488r~+>&j>>33RCNGP<*P)E(p&24W@T2@x0qdFnr znxZb>D7ks<^PF1{ZT8C{1d-2-sSmd1_g_6yC-^1X#)#eTOQuDb{j|csBjW=uSZ2~b z3-&J`Kki(Q$`B0vW77;Zoo~@DQ)rQDUO?;-$eXNJ81X9@Bn#AQ<`l{McWeuJ|2EZD z6=9Z1L$_|ouez${_~hiq&(7iz((GW;+>Z@DfAL}mlyBL)C=RgsVvmR^Q~+)=PCgU1 zE2Qc>wEdpvW2o6V)>pBy-1C=Z>n~to>M|#%T}c}U9zNX4V#HCD>oS=nblU$yr>&^n zy2lB1ys3RxkI;HbbJ5MTKQd#`ZjhLRSSIJjVH~)i?;f~J6%~~^&E?D7w0xP2tgH?1 zInYdiERm6g<^IWMvCg0do8)4*p4HNV`f)t^3uHP7l7mwn27lUelfbH{y={9oCM89g zK0}D*4yoh zZ!*`-wNK=G@+f)XCXL=oN2rY7Ny13g;|yCd&s&n#b2tof5?+-DmXEYTZbi5vopGbVOoYtVPs z8mD9aVPNEuYwW$e7zU0~>>k>6ku?s(?Z+${fRT4=TuKSfj6=7s;~qQ$68EDV(%ZMk zyEw7|%l**J*WNdVE>6-dS?{)xp~6L7@jzoiXLO7X90P;gU(z*2q-}n767#IbzkS$x zr+AC^$qcKDu&ma*^U)M+B8E2i|5}Zzo-vc%kJ9Bs6W?;HWc(mB zvv#Ic(Jw4(oFoJwx}{0!36!NhD}>Yh=I-yMu6vm&f$2r7&qi zd&;$7hx^p7c{V$c_ett9!dK$ZY8qhnECV@H*#rz?BWogBATU1kXRKj>B*F9eR5ey# zcV6wn1uwoFYS~=c%Wxlc9}is&=Hz4u6ufuxSq^8Wz3(-f6nqectn@L}0TQYkcGwRz z!N2J5d3o7oV52LE`VMzdi4Xjl3IEBt;4=6`J6JU#kP+tAN~G6Y9)k%ny>4<6rWB1| z#s`vPVmNz?!G=p5+zE1>iLNnPON5fUg~_HdSZ~?A>IgDfSXs5@IwV^*3k^gVkS0h@ z$|!9_ofWhDi7Ikx%C$QJCf((aP_l4cT*#kj$5T?hV4MzZ&{~tXR9rR?9kQ(7 zp!P1Cwf(Ulg;H*%2^#Vr3r@rNNwcS*A$p_{N7h)l4`I*4#wT(^=aY3YRn-l|Ep!HT z(-VleYP0g>aiY%%jH{6z)C@=dZK;glzReb`A$NM2s%7CSD)9}UO`kmh`@^(HS;BF6 z=X`8wNeNW%HPqE#N)%|HRskvMVH~)MAL77+GO~`bp>LNRadb5{{sG@g;y~TH%y9!W z8L)`WS(%yVU0U@Ia+RlT9QN5EWoiDgsfn8zT+<5+O4 z*wXkf>Vc+g>+X9(M-GKmNzMOKfnh*DDIo?SCzp`{<6N>kK|SSd@FZIQ6q*sBnVC5Z zkoG(z#E}t_n8c&qO`1XH+!diP(8%h(e3^n_lxFXMjm^ONgHUUk1#jB&6|5YgYeh|) zh7mj_TR5TB^N>Y#w{@aU?)hBObK{{AM_ZZ!A}|^lz(o?K&3d4+OnyZkteSB%qOi25 zPmkqJm~2mn8uVMc{xZ%{T5BrKh^IL4EOgtZ7(cSrPa~Rs#-%!3Mex2A!nn4Z}F+L^Bf=>R5F>V;`PXy?0U` zE~r#&E>!3`{ZNnG7NFlsqzWESB^X8==g>PS8`FtE4Vw$DOHyI-;lqc58h&)%Cy`ER zK~3O(EGT0S?R(Ma2Xiju12RyZkTD1kFJFFSeFd3$xT;)Xt^w7&@S>h#k~Z^Jkt8Vhzap|JO>%m~ZsKHR}vI94)!-7qa1CYSjT zIlZggO%9Z<(-pQYB5NW9tiipuwY?kOR~LpwzhVtGe{U}Q8hS9L+~>X+rkT{I3P#l$ zbJ5jRQTgZTT7d#%WCUIRLQAH$7=B5~1x&XX9JFkq%MlzveA0k^ist57B35t+O>`7z zTYrn+wM--!AuiqfUfb$Vt2VsRKdT%7ZZWtiuUjnL$y!bxPTpj1pN;J~4x5CAaFhC6 zD8D$0SLc%5Cs{;lJE`Y1veP>6@^id>%gq?B5oE_2xf_JLl3#;h(c~Q_ANr$Yuw_Cd zYQym7B%RrraWh>;@;^OU(oH@w%fh&0#}KqQdV8HF2X%PpyRMMqN(x!B?_2H<-suaC z$6MfBkqoB(SV2Kw_wlcP*t*g&QXQ)2+=m2Jc!rpmp#hD*NiEPb~MadB~#xbp{?y zL@GHU;l9;A3VllI0rCw|@E`LKW)KwyD);L%yGZ`Rq4oBji_ zREnY8BNtX=XK#ZS+tu~q+5#%gGA>_S)lb(C*h6mvIxN{wpI+40-%0D~O6q51 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 HJNJJ8BCL|} From 297b57ab5989d8054b0243d6ff558ae0e3471df1 Mon Sep 17 00:00:00 2001 From: dimiexplorer Date: Mon, 9 Sep 2024 16:03:51 +1000 Subject: [PATCH 072/369] chore: update copy with feedback --- pages/pipelines/hosted_agents/mac.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pages/pipelines/hosted_agents/mac.md b/pages/pipelines/hosted_agents/mac.md index 3251e340e5..4bd1b31c3a 100644 --- a/pages/pipelines/hosted_agents/mac.md +++ b/pages/pipelines/hosted_agents/mac.md @@ -153,13 +153,9 @@ Updated Xcode versions will be available one week after Apple offers them for do - libpq - GMP -## Cache volumes +## Git mirror cache -_Cache volumes_ are external volumes attached to hosted agent instances. These volumes are attached on a best-effort basis depending on their locality, expiration and current usage, and therefore, should not be relied upon as durable data storage. By default, cache volumes are scoped to a pipeline and are shared between all steps in the pipeline. - -### Git mirror cache - -The Git mirror cache is a special type of cache volume that is used to speed up Git operations by caching the Git repository between builds. This is useful for large repositories that are slow to clone. +The Git mirror cache is a specialized type of cache volume designed to accelerate Git operation by caching the Git repository between builds. This is useful for large repositories that are slow to clone. These volumes are attached on a best-effort basis depending on their locality, expiration and current usage, and therefore, should not be relied upon as durable data storage. By default, Git mirror cache is scoped to a pipeline and is shared between all steps in the pipeline. ### Enabling Git mirror cache From 2dd1aeeb015519eae0a9a6ded32af8e5fc4f6af3 Mon Sep 17 00:00:00 2001 From: dimiexplorer Date: Mon, 9 Sep 2024 16:58:26 +1000 Subject: [PATCH 073/369] chore: update min cache size --- pages/pipelines/hosted_agents/linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/hosted_agents/linux.md b/pages/pipelines/hosted_agents/linux.md index edc13133fe..c135809c54 100644 --- a/pages/pipelines/hosted_agents/linux.md +++ b/pages/pipelines/hosted_agents/linux.md @@ -100,7 +100,7 @@ Optional attributes: size - The size of the cache volume. The default size is 20 gigabytes. Units are in gigabytes, specified as Ng, where N is the size in gigabytes, and g indicates gigabytes.
+ The size of the cache volume. The default size is 20 gigabytes. A minimum of 20 gigabytes of cache can be requested.
Units are in gigabytes, specified as Ng, where N is the size in gigabytes, and g indicates gigabytes.
Example: "20g"
From 97da0ea1ac92fff6aa6ff00b2d8bbe5b8bc4f38e Mon Sep 17 00:00:00 2001 From: dimiexplorer Date: Mon, 9 Sep 2024 17:25:42 +1000 Subject: [PATCH 074/369] copy: specify cache created for each repo Co-authored-by: Matthew Borden --- pages/pipelines/hosted_agents/mac.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/pipelines/hosted_agents/mac.md b/pages/pipelines/hosted_agents/mac.md index 4bd1b31c3a..b89b8bfe11 100644 --- a/pages/pipelines/hosted_agents/mac.md +++ b/pages/pipelines/hosted_agents/mac.md @@ -155,7 +155,7 @@ Updated Xcode versions will be available one week after Apple offers them for do ## Git mirror cache -The Git mirror cache is a specialized type of cache volume designed to accelerate Git operation by caching the Git repository between builds. This is useful for large repositories that are slow to clone. These volumes are attached on a best-effort basis depending on their locality, expiration and current usage, and therefore, should not be relied upon as durable data storage. By default, Git mirror cache is scoped to a pipeline and is shared between all steps in the pipeline. +The Git mirror cache is a specialized type of cache volume designed to accelerate Git operation by caching the Git repository between builds. This is useful for large repositories that are slow to clone. These volumes are attached on a best-effort basis depending on their locality, expiration and current usage, and therefore, should not be relied upon as durable data storage. By default, a Git mirror cache is created for each repository. ### Enabling Git mirror cache From 3d0db32a91a2b1b58919a282c5ed5fe662774d40 Mon Sep 17 00:00:00 2001 From: ivannalisetska <155262606+ivannalisetska@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:16:08 -0600 Subject: [PATCH 075/369] Update pages/agent/v3/signed_pipelines.md Co-authored-by: Josh Deprez --- pages/agent/v3/signed_pipelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/agent/v3/signed_pipelines.md b/pages/agent/v3/signed_pipelines.md index c32949cecc..dd39302bf5 100644 --- a/pages/agent/v3/signed_pipelines.md +++ b/pages/agent/v3/signed_pipelines.md @@ -106,7 +106,7 @@ This ensures that whenever those agents upload steps to Buildkite, they'll gener verification-failure-behavior= ``` -This setting determines the BuildKite agent’s response when it receives a job without a proper signature. It specifies how strictly the agent should enforce signature verification for incoming jobs. The agent will warn about the missing signature but will still proceed with executing the job. If not explicitly specified, the default behavior is `block`, which will prevent any job without a signature from running, ensuring a secure pipeline environment by default. +This setting determines the Buildkite agent’s response when it receives a job without a proper signature. It specifies how strictly the agent should enforce signature verification for incoming jobs. The agent will warn about the missing signature but will still proceed with executing the job. If not explicitly specified, the default behavior is `block`, which will prevent any job without a signature from running, ensuring a secure pipeline environment by default. From dd755d4343735cc0ff142b296d89b2ebf607837a Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Wed, 11 Sep 2024 11:21:22 +1000 Subject: [PATCH 076/369] Add more introductory content to the Buildkite platform docs landing page. --- pages/platform.md | 8 +++++++- pages/team_management.md | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pages/platform.md b/pages/platform.md index 565ceb0f98..b1ffffcae1 100644 --- a/pages/platform.md +++ b/pages/platform.md @@ -6,4 +6,10 @@ template: "landing_page" The Buildkite Scale-Out Delivery Platform is an adaptable, composable, and scalable platform with everything platform teams need to build software delivery systems for their businesses—and rapidly deliver value to users. -The Buildkite platform documentation contains docs for _platform_-level features of Buildkite that can apply to Buildkite [Pipelines](/docs/pipelines), [Test Engine](/docs/test-analytics), as well as [Packages](/docs/packages). +The Buildkite platform documentation contains docs for _platform_-level features of Buildkite available across Buildkite [Pipelines](/docs/pipelines), [Test Engine](/docs/test-analytics), as well as [Packages](/docs/packages). This area of the docs covers the following topics: + +- [Team management](/docs/team-management), with guidelines on how to manage your users and teams across the Buildkite platform for Pipelines, Test Engine and Packages. + +- [Buildkite CLI](/docs/cli), provides command line/terminal access to work with features across the Buildkite platform. + +- [Single sign-on (SSO)](/docs/integrations/sso) provides guidelines on how to protect access to your Buildkite organization using a supported third-party SSO provider. diff --git a/pages/team_management.md b/pages/team_management.md index 5aaec37e9d..aa7594653f 100644 --- a/pages/team_management.md +++ b/pages/team_management.md @@ -4,7 +4,7 @@ toc: false # Team management -Managing users and teams in CI/CD is fundamental to collaboration, streamlined processes, and ensuring adequate access controls. Buildkite provides features to manage team access: +Managing users and teams across your CI/CD platform is fundamental to collaboration, streamlined processes, and ensuring adequate access controls. Buildkite provides features to manage team access: - [User and team permissions](/docs/team-management/permissions) - [Enforce 2FA](/docs/team-management/enforce-2fa) From 8650fbc5e7261e71ccfe9c3c825ffc5a1f936971 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Wed, 11 Sep 2024 11:24:49 +1000 Subject: [PATCH 077/369] Move 'Files' button on Packages landing page. --- pages/packages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/packages.md b/pages/packages.md index ddd0c0e8a5..08f8359e59 100644 --- a/pages/packages.md +++ b/pages/packages.md @@ -29,6 +29,7 @@ If you're familiar with the basics, explore how to use registries for each of Bu <%= button ":alpine: Alpine (apk)", "/docs/packages/alpine" %> <%= button ":docker: Container (Docker)", "/docs/packages/container" %> <%= button ":debian: Debian/Ubuntu (deb)", "/docs/packages/debian" %> + <%= button ":package: Files (generic)", "/docs/packages/files" %> <%= button ":helm: Helm (OCI)", "/docs/packages/helm-oci" %> <%= button ":helm: Helm", "/docs/packages/helm" %> <%= button ":maven: Java (Maven)", "/docs/packages/maven" %> @@ -38,7 +39,6 @@ If you're familiar with the basics, explore how to use registries for each of Bu <%= button ":redhat: Red Hat (RPM)", "/docs/packages/red-hat" %> <%= button ":ruby: Ruby (RubyGems)", "/docs/packages/ruby" %> <%= button ":terraform: Terraform (modules)", "/docs/packages/terraform" %> - <%= button ":package: Files (generic)", "/docs/packages/files" %>

From 536389016582cd1f3333f079cd2b2dcd9fb2d024 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Wed, 11 Sep 2024 14:49:21 +1000 Subject: [PATCH 078/369] Git mirror cache for Mac minor wording and formatting tweaks. --- pages/pipelines/hosted_agents/linux.md | 8 ++++---- pages/pipelines/hosted_agents/mac.md | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pages/pipelines/hosted_agents/linux.md b/pages/pipelines/hosted_agents/linux.md index c135809c54..fe4ab1e190 100644 --- a/pages/pipelines/hosted_agents/linux.md +++ b/pages/pipelines/hosted_agents/linux.md @@ -71,7 +71,7 @@ steps: ``` {: codeblock-file="pipeline.yml"} -Required attributes: +#### Required attributes @@ -86,7 +86,7 @@ Required attributes:
-Optional attributes: +#### Optional attributes @@ -100,7 +100,7 @@ Optional attributes: @@ -128,7 +128,7 @@ An additional (smaller) charge is made per gigabyte of _active cache_, where act ### Git mirror cache -The Git mirror cache is a special type of cache volume that is used to speed up Git operations by caching the Git repository between builds. This is useful for large repositories that are slow to clone. +The Git mirror cache is a specialized type of cache volume designed to accelerate Git operations by caching the Git repository between builds. This is useful for large repositories that are slow to clone. Git mirror caching can be enabled on the cluster's cache volumes settings page. Once enabled, the Git mirror cache will be used for all hosted jobs in that cluster. A separate cache volume will be created for each repository. diff --git a/pages/pipelines/hosted_agents/mac.md b/pages/pipelines/hosted_agents/mac.md index b89b8bfe11..c737443804 100644 --- a/pages/pipelines/hosted_agents/mac.md +++ b/pages/pipelines/hosted_agents/mac.md @@ -155,14 +155,16 @@ Updated Xcode versions will be available one week after Apple offers them for do ## Git mirror cache -The Git mirror cache is a specialized type of cache volume designed to accelerate Git operation by caching the Git repository between builds. This is useful for large repositories that are slow to clone. These volumes are attached on a best-effort basis depending on their locality, expiration and current usage, and therefore, should not be relied upon as durable data storage. By default, a Git mirror cache is created for each repository. +The Git mirror cache is a specialized type of cache volume designed to accelerate Git operations by caching the Git repository between builds. This is useful for large repositories that are slow to clone. + +These volumes are attached on a best-effort basis depending on their locality, expiration and current usage, and therefore, should not be relied upon as durable data storage. By default, a Git mirror cache is created for each repository. ### Enabling Git mirror cache To enable Git mirror cache for your hosted agents: 1. Select **Agents** in the global navigation to access the **Clusters** page. -1. Select the cluster in which to enable Git mirror cache. +1. Select the cluster in which to enable the Git mirror cache feature. 1. Select **Cache Storage**, then select the **Settings** tab. 1. Select **Enable Git mirror**, then select **Save cache settings** to enable Git mirrors for the selected hosted cluster. @@ -177,7 +179,7 @@ Deleting a cache volume may affect the build time for the associated pipelines u To delete a git mirror cache: 1. Select **Agents** in the global navigation to access the **Clusters** page. -1. Select the cluster in which to delete Git mirror cache. +1. Select the cluster whose Git mirror cache is to be deleted. 1. Select **Cache Storage**, then select the **Volumes** tab to view a list of all exiting cache volumes. 1. Select **Delete** for the Git mirror cache volume you wish to remove. 1. Confirm the deletion by selecting **Delete Cache Volume**. From a5bfbf77c96a23437c1cc49223c34627b2b69595 Mon Sep 17 00:00:00 2001 From: Giles Gas Date: Wed, 11 Sep 2024 15:45:41 +1000 Subject: [PATCH 079/369] Update pages/agent/v3/signed_pipelines.md Co-authored-by: Josh Deprez --- pages/agent/v3/signed_pipelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/agent/v3/signed_pipelines.md b/pages/agent/v3/signed_pipelines.md index dd39302bf5..9dc749559c 100644 --- a/pages/agent/v3/signed_pipelines.md +++ b/pages/agent/v3/signed_pipelines.md @@ -106,7 +106,7 @@ This ensures that whenever those agents upload steps to Buildkite, they'll gener verification-failure-behavior= ``` -This setting determines the Buildkite agent’s response when it receives a job without a proper signature. It specifies how strictly the agent should enforce signature verification for incoming jobs. The agent will warn about the missing signature but will still proceed with executing the job. If not explicitly specified, the default behavior is `block`, which will prevent any job without a signature from running, ensuring a secure pipeline environment by default. +This setting determines the Buildkite agent’s response when it receives a job without a proper signature. It specifies how strictly the agent should enforce signature verification for incoming jobs. The agent will warn about missing or invalid signatures, but will still proceed with executing the job. If not explicitly specified, the default behavior is `block`, which will prevent any job without a valid signature from running, ensuring a secure pipeline environment by default. From a55f56e64dd712e5156b62b708d96840f316438a Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:04:59 +0900 Subject: [PATCH 080/369] docs for registries index --- data/nav.yml | 6 +++- pages/apis/rest_api/packages/registries.md | 35 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 pages/apis/rest_api/packages/registries.md diff --git a/data/nav.yml b/data/nav.yml index 605e8cae2e..0f75cd6020 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -557,7 +557,11 @@ - name: "Members" path: "apis/rest-api/organizations/members" pill: "beta" - - name: "Pipelines " + - name: "Packages" + children: + - name: "Registries" + path: "apis/rest-api/packages/registries" + - name: "Pipelines" children: - name: "Overview" path: "apis/rest-api/pipelines" diff --git a/pages/apis/rest_api/packages/registries.md b/pages/apis/rest_api/packages/registries.md new file mode 100644 index 0000000000..33d84ee184 --- /dev/null +++ b/pages/apis/rest_api/packages/registries.md @@ -0,0 +1,35 @@ +# Registries API + +The registries API lets you create and manage registries in your organization. + +## List all registries + +Returns a list of an organization's registries. + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X GET "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries" +``` + +```json +[ + { + "id": "0191df84-85e4-77aa-83ba-6579084728eb", + "graphql_id": "UmVnaXN0cnktLS0wMTkxZGY4NC04NWU0LTc3YWEtODNiYS02NTc5MDg0NzI4ZWI=", + "slug": "my-registry", + "url": "https://api.buildkite.com/v2/packages/organizations/my-org/registries/my-registry", + "web_url": "https://buildkite.com/organizations/my-org/packages/registries/my-registry", + "name": "my registry", + "ecosystem": "ruby", + "description": "registry containing ruby gems", + "emoji": null, + "color": null, + "public": false, + "oidc_policy": null + } +] +``` + +Required scope: `read_registries` + +Success response: `200 OK` From edc77aab1401d5c2b024673a2ef3e208fdd77831 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:29:04 +0900 Subject: [PATCH 081/369] api docs for registry show --- pages/apis/rest_api/packages/registries.md | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pages/apis/rest_api/packages/registries.md b/pages/apis/rest_api/packages/registries.md index 33d84ee184..33b264021b 100644 --- a/pages/apis/rest_api/packages/registries.md +++ b/pages/apis/rest_api/packages/registries.md @@ -33,3 +33,33 @@ curl -H "Authorization: Bearer $TOKEN" \ Required scope: `read_registries` Success response: `200 OK` + +## Get a registry + +Returns the details for a single registry, looked up by its slug. + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X GET "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}" +``` + +```json +{ + "id": "0191df84-85e4-77aa-83ba-6579084728eb", + "graphql_id": "UmVnaXN0cnktLS0wMTkxZGY4NC04NWU0LTc3YWEtODNiYS02NTc5MDg0NzI4ZWI=", + "slug": "my-registry", + "url": "https://api.buildkite.com/v2/packages/organizations/my-org/registries/my-registry", + "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", + "name": "my registry", + "ecosystem": "ruby", + "description": "registry containing ruby gems", + "emoji": null, + "color": null, + "public": false, + "oidc_policy": null +} +``` + +Required scope: `read_registries` + +Success response: `200 OK` \ No newline at end of file From 49e5edd9f1e1835d66ced64a6ee394e6d45f9807 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:00:51 +0900 Subject: [PATCH 082/369] api docs for registry create --- pages/apis/rest_api/packages/registries.md | 54 +++++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/pages/apis/rest_api/packages/registries.md b/pages/apis/rest_api/packages/registries.md index 33b264021b..765716bd1e 100644 --- a/pages/apis/rest_api/packages/registries.md +++ b/pages/apis/rest_api/packages/registries.md @@ -2,6 +2,56 @@ The registries API lets you create and manage registries in your organization. +## Create a registry + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X POST "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "my registry", + "ecosystem": "ruby" + }' +``` + +```json +{ + "id": "0191df84-85e4-77aa-83ba-6579084728eb", + "graphql_id": "UmVnaXN0cnktLS0wMTkxZGY4NC04NWU0LTc3YWEtODNiYS02NTc5MDg0NzI4ZWI=", + "slug": "my-registry", + "url": "https://api.buildkite.com/v2/packages/organizations/my-org/registries/my-registry", + "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", + "name": "my registry", + "ecosystem": "ruby", + "description": null, + "emoji": null, + "color": null, + "public": false, + "oidc_policy": null +} +``` + +Required [request body properties](/docs/api#request-body-properties): + +
size - The size of the cache volume. The default size is 20 gigabytes. A minimum of 20 gigabytes of cache can be requested.
Units are in gigabytes, specified as Ng, where N is the size in gigabytes, and g indicates gigabytes.
+ The size of the cache volume. The default size is 20 gigabytes, which is also the minimum cache size that can be requested.
Units are in gigabytes, specified as Ng, where N is the size in gigabytes, and g indicates gigabytes.
Example: "20g"
+ + + + +
nameName of the new registry.
Example: "my registry".
ecosystemRegistry ecosystem based on the package ecosystem for the new registry.
Example: "ruby".
+ +Optional [request body properties](/docs/api#request-body-properties): + + + + + +
descriptionDescription of the registry.
Default value: null.
+ +Required scope: `create_registries` + +Success response: `200 OK` + ## List all registries Returns a list of an organization's registries. @@ -21,7 +71,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "web_url": "https://buildkite.com/organizations/my-org/packages/registries/my-registry", "name": "my registry", "ecosystem": "ruby", - "description": "registry containing ruby gems", + "description": null, "emoji": null, "color": null, "public": false, @@ -52,7 +102,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", "name": "my registry", "ecosystem": "ruby", - "description": "registry containing ruby gems", + "description": null, "emoji": null, "color": null, "public": false, From cb5e072aad65d65d2f61b7532e849a117be4d8e5 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:14:12 +0900 Subject: [PATCH 083/369] api docs for registry update --- pages/apis/rest_api/packages/registries.md | 51 ++++++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/pages/apis/rest_api/packages/registries.md b/pages/apis/rest_api/packages/registries.md index 765716bd1e..74cf1eea83 100644 --- a/pages/apis/rest_api/packages/registries.md +++ b/pages/apis/rest_api/packages/registries.md @@ -10,7 +10,8 @@ curl -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "my registry", - "ecosystem": "ruby" + "ecosystem": "ruby", + "description": "registry containing ruby gems" }' ``` @@ -23,7 +24,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", "name": "my registry", "ecosystem": "ruby", - "description": null, + "description": "registry containing ruby gems", "emoji": null, "color": null, "public": false, @@ -71,7 +72,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "web_url": "https://buildkite.com/organizations/my-org/packages/registries/my-registry", "name": "my registry", "ecosystem": "ruby", - "description": null, + "description": "registry containing ruby gems", "emoji": null, "color": null, "public": false, @@ -102,7 +103,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", "name": "my registry", "ecosystem": "ruby", - "description": null, + "description": "registry containing ruby gems", "emoji": null, "color": null, "public": false, @@ -112,4 +113,46 @@ curl -H "Authorization: Bearer $TOKEN" \ Required scope: `read_registries` +Success response: `200 OK` + +## Update a registry + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X POST "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "my registry", + "description": "registry containing ruby gems" + }' +``` + +```json +{ + "id": "0191df84-85e4-77aa-83ba-6579084728eb", + "graphql_id": "UmVnaXN0cnktLS0wMTkxZGY4NC04NWU0LTc3YWEtODNiYS02NTc5MDg0NzI4ZWI=", + "slug": "my-registry", + "url": "https://api.buildkite.com/v2/packages/organizations/my-org/registries/my-registry", + "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", + "name": "my registry", + "ecosystem": "ruby", + "description": "registry containing ruby gems", + "emoji": null, + "color": null, + "public": false, + "oidc_policy": null +} +``` + +Optional [request body properties](/docs/api#request-body-properties): + + + + + + +
nameName of the registry.
Example: my registry.
descriptionDescription of the registry.
Example: registry containing ruby gems.
+ +Required scope: `write_registries` + Success response: `200 OK` \ No newline at end of file From e0172bf744c2afabe052993bcf60e3defb0c4687 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:29:04 +0900 Subject: [PATCH 084/369] api docs for registry show --- pages/apis/rest_api/packages/registries.md | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pages/apis/rest_api/packages/registries.md b/pages/apis/rest_api/packages/registries.md index 33d84ee184..2c95bd3e0c 100644 --- a/pages/apis/rest_api/packages/registries.md +++ b/pages/apis/rest_api/packages/registries.md @@ -33,3 +33,33 @@ curl -H "Authorization: Bearer $TOKEN" \ Required scope: `read_registries` Success response: `200 OK` + +## Get a registry + +Returns the details for a single registry, looked up by its slug. + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X GET "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}" +``` + +```json +{ + "id": "0191df84-85e4-77aa-83ba-6579084728eb", + "graphql_id": "UmVnaXN0cnktLS0wMTkxZGY4NC04NWU0LTc3YWEtODNiYS02NTc5MDg0NzI4ZWI=", + "slug": "my-registry", + "url": "https://api.buildkite.com/v2/packages/organizations/my-org/registries/my-registry", + "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", + "name": "my registry", + "ecosystem": "ruby", + "description": "registry containing ruby gems", + "emoji": null, + "color": null, + "public": false, + "oidc_policy": null +} +``` + +Required scope: `read_registries` + +Success response: `200 OK` From 77f9ad5665a2d074743247aca4cb03f335266b5d Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:00:51 +0900 Subject: [PATCH 085/369] api docs for registry create --- pages/apis/rest_api/packages/registries.md | 56 ++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/pages/apis/rest_api/packages/registries.md b/pages/apis/rest_api/packages/registries.md index 33b264021b..b5c37cbad1 100644 --- a/pages/apis/rest_api/packages/registries.md +++ b/pages/apis/rest_api/packages/registries.md @@ -2,6 +2,56 @@ The registries API lets you create and manage registries in your organization. +## Create a registry + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X POST "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "my registry", + "ecosystem": "ruby" + }' +``` + +```json +{ + "id": "0191df84-85e4-77aa-83ba-6579084728eb", + "graphql_id": "UmVnaXN0cnktLS0wMTkxZGY4NC04NWU0LTc3YWEtODNiYS02NTc5MDg0NzI4ZWI=", + "slug": "my-registry", + "url": "https://api.buildkite.com/v2/packages/organizations/my-org/registries/my-registry", + "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", + "name": "my registry", + "ecosystem": "ruby", + "description": null, + "emoji": null, + "color": null, + "public": false, + "oidc_policy": null +} +``` + +Required [request body properties](/docs/api#request-body-properties): + + + + + + +
nameName of the new registry.
Example: "my registry".
ecosystemRegistry ecosystem based on the package ecosystem for the new registry.
Example: "ruby".
+ +Optional [request body properties](/docs/api#request-body-properties): + + + + + +
descriptionDescription of the registry.
Default value: null.
+ +Required scope: `create_registries` + +Success response: `200 OK` + ## List all registries Returns a list of an organization's registries. @@ -21,7 +71,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "web_url": "https://buildkite.com/organizations/my-org/packages/registries/my-registry", "name": "my registry", "ecosystem": "ruby", - "description": "registry containing ruby gems", + "description": null, "emoji": null, "color": null, "public": false, @@ -52,7 +102,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", "name": "my registry", "ecosystem": "ruby", - "description": "registry containing ruby gems", + "description": null, "emoji": null, "color": null, "public": false, @@ -62,4 +112,4 @@ curl -H "Authorization: Bearer $TOKEN" \ Required scope: `read_registries` -Success response: `200 OK` \ No newline at end of file +Success response: `200 OK` From ca53b43eec8e7a630aef8644a1ffe877b9cd8fe2 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:14:12 +0900 Subject: [PATCH 086/369] api docs for registry update --- pages/apis/rest_api/packages/registries.md | 53 ++++++++++++++++++++-- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/pages/apis/rest_api/packages/registries.md b/pages/apis/rest_api/packages/registries.md index 765716bd1e..33fc90093a 100644 --- a/pages/apis/rest_api/packages/registries.md +++ b/pages/apis/rest_api/packages/registries.md @@ -10,7 +10,8 @@ curl -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "my registry", - "ecosystem": "ruby" + "ecosystem": "ruby", + "description": "registry containing ruby gems" }' ``` @@ -23,7 +24,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", "name": "my registry", "ecosystem": "ruby", - "description": null, + "description": "registry containing ruby gems", "emoji": null, "color": null, "public": false, @@ -71,7 +72,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "web_url": "https://buildkite.com/organizations/my-org/packages/registries/my-registry", "name": "my registry", "ecosystem": "ruby", - "description": null, + "description": "registry containing ruby gems", "emoji": null, "color": null, "public": false, @@ -102,7 +103,7 @@ curl -H "Authorization: Bearer $TOKEN" \ "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", "name": "my registry", "ecosystem": "ruby", - "description": null, + "description": "registry containing ruby gems", "emoji": null, "color": null, "public": false, @@ -112,4 +113,46 @@ curl -H "Authorization: Bearer $TOKEN" \ Required scope: `read_registries` -Success response: `200 OK` \ No newline at end of file +Success response: `200 OK` + +## Update a registry + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X POST "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "my registry", + "description": "registry containing ruby gems" + }' +``` + +```json +{ + "id": "0191df84-85e4-77aa-83ba-6579084728eb", + "graphql_id": "UmVnaXN0cnktLS0wMTkxZGY4NC04NWU0LTc3YWEtODNiYS02NTc5MDg0NzI4ZWI=", + "slug": "my-registry", + "url": "https://api.buildkite.com/v2/packages/organizations/my-org/registries/my-registry", + "web_url": "https://buildkite.com/organizations/my-org/registries/my-registry", + "name": "my registry", + "ecosystem": "ruby", + "description": "registry containing ruby gems", + "emoji": null, + "color": null, + "public": false, + "oidc_policy": null +} +``` + +Optional [request body properties](/docs/api#request-body-properties): + + + + + + +
nameName of the registry.
Example: my registry.
descriptionDescription of the registry.
Example: registry containing ruby gems.
+ +Required scope: `write_registries` + +Success response: `200 OK` From ecafb614b731d80b6dfa8fa94a80976f59dd238f Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:18:10 +0900 Subject: [PATCH 087/369] api docs for registry delete --- pages/apis/rest_api/packages/registries.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pages/apis/rest_api/packages/registries.md b/pages/apis/rest_api/packages/registries.md index 74cf1eea83..cdfb0e3cfa 100644 --- a/pages/apis/rest_api/packages/registries.md +++ b/pages/apis/rest_api/packages/registries.md @@ -155,4 +155,15 @@ Optional [request body properties](/docs/api#request-body-properties): Required scope: `write_registries` -Success response: `200 OK` \ No newline at end of file +Success response: `200 OK` + +## Delete a registry + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X DELETE "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}" +``` + +Required scope: `delete_registries` + +Success response: `200 OK` From 2028687d4016860d5c8469e14c7f8e32f7bcae33 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Wed, 11 Sep 2024 19:36:23 +0900 Subject: [PATCH 088/369] api docs for registry tokens index --- data/nav.yml | 6 ++- .../apis/rest_api/packages/registry_tokens.md | 50 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 pages/apis/rest_api/packages/registry_tokens.md diff --git a/data/nav.yml b/data/nav.yml index 605e8cae2e..0655d53365 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -557,7 +557,11 @@ - name: "Members" path: "apis/rest-api/organizations/members" pill: "beta" - - name: "Pipelines " + - name: "Packages" + children: + - name: "Registry Tokens" + path: "apis/rest-api/packages/registry-tokens" + - name: "Pipelines" children: - name: "Overview" path: "apis/rest-api/pipelines" diff --git a/pages/apis/rest_api/packages/registry_tokens.md b/pages/apis/rest_api/packages/registry_tokens.md new file mode 100644 index 0000000000..eb9047c82a --- /dev/null +++ b/pages/apis/rest_api/packages/registry_tokens.md @@ -0,0 +1,50 @@ +# Registry Tokens API + +The registry tokens API lets you create and manage credentials needed to install and use packages in a registry. + +## List all registry tokens + +Returns a list of a registry's tokens. + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X POST "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}/tokens" \ + -H "Content-Type: application/json" +``` + +```json +[ + { + "id": "0191b6a2-aa51-70d0-8a5f-aabce115b0fd", + "graphql_id": "UmVnaXN0cnlUb2tlbi0tLTAxOTFiNmEyLWFhNTEtNzBkMC04YTVmLWFhYmNlMTE1YjBmZA==", + "description": "Usher", + "url": "http://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry/tokens/0191b6a2-aa51-70d0-8a5f-aabce115b0fd", + "created_at": "2024-09-03T06:46:39.441Z", + "created_by": { + "id": "0191b13b-0eb6-470d-a4c0-2085974f3580", + "graphql_id": "VXNlci0tLTAxOTFiMTNiLTBlYjYtNDcwZC1hNGMwLTIwODU5NzRmMzU4MA==", + "name": "Eminem", + "email": "eminem@buildkite.com", + "avatar_url": null, + "created_at": "2024-09-02T05:35:23.318Z" + }, + "organization": { + "id": "018a456f-e581-44b6-c5a4-1d8a5f7094ee", + "slug": "my_great_org", + "url": "https://api.buildkite.com/v2/analytics/organizations/my_great_org", + "web_url": "https://buildkite.com/organizations/my_great_org" + }, + "registry": { + "id": "018f56ef-9ef4-70f0-aba2-0f4578e3d69d", + "graphql_id": "UmVnaXN0cnktLS0wMThmNTZlZi05ZWY0LTcwZjAtYWJhMi0wZjQ1NzhlM2Q2OWQ=", + "slug": "my-registry", + "url": "http://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry", + "web_url": "http://buildkite.com/organizations/buildkite/my_great_org/registries/my-registry" + } + } +] +``` + +Required scope: `read_registries` + +Success response: `200 OK` From f8a14c0df9c7d82e1ac7d28d93854ec02ec7ed72 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Wed, 11 Sep 2024 19:36:23 +0900 Subject: [PATCH 089/369] api docs for registry tokens index --- data/nav.yml | 6 ++- .../apis/rest_api/packages/registry_tokens.md | 50 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 pages/apis/rest_api/packages/registry_tokens.md diff --git a/data/nav.yml b/data/nav.yml index 605e8cae2e..0655d53365 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -557,7 +557,11 @@ - name: "Members" path: "apis/rest-api/organizations/members" pill: "beta" - - name: "Pipelines " + - name: "Packages" + children: + - name: "Registry Tokens" + path: "apis/rest-api/packages/registry-tokens" + - name: "Pipelines" children: - name: "Overview" path: "apis/rest-api/pipelines" diff --git a/pages/apis/rest_api/packages/registry_tokens.md b/pages/apis/rest_api/packages/registry_tokens.md new file mode 100644 index 0000000000..e8a1ce7965 --- /dev/null +++ b/pages/apis/rest_api/packages/registry_tokens.md @@ -0,0 +1,50 @@ +# Registry Tokens API + +The registry tokens API lets you create and manage credentials needed to install and use packages in a registry. + +## List all registry tokens + +Returns a list of a registry's tokens. + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X GET "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}/tokens" \ + -H "Content-Type: application/json" +``` + +```json +[ + { + "id": "0191b6a2-aa51-70d0-8a5f-aabce115b0fd", + "graphql_id": "UmVnaXN0cnlUb2tlbi0tLTAxOTFiNmEyLWFhNTEtNzBkMC04YTVmLWFhYmNlMTE1YjBmZA==", + "description": "Usher", + "url": "http://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry/tokens/0191b6a2-aa51-70d0-8a5f-aabce115b0fd", + "created_at": "2024-09-03T06:46:39.441Z", + "created_by": { + "id": "0191b13b-0eb6-470d-a4c0-2085974f3580", + "graphql_id": "VXNlci0tLTAxOTFiMTNiLTBlYjYtNDcwZC1hNGMwLTIwODU5NzRmMzU4MA==", + "name": "Eminem", + "email": "eminem@buildkite.com", + "avatar_url": null, + "created_at": "2024-09-02T05:35:23.318Z" + }, + "organization": { + "id": "018a456f-e581-44b6-c5a4-1d8a5f7094ee", + "slug": "my_great_org", + "url": "https://api.buildkite.com/v2/analytics/organizations/my_great_org", + "web_url": "https://buildkite.com/organizations/my_great_org" + }, + "registry": { + "id": "018f56ef-9ef4-70f0-aba2-0f4578e3d69d", + "graphql_id": "UmVnaXN0cnktLS0wMThmNTZlZi05ZWY0LTcwZjAtYWJhMi0wZjQ1NzhlM2Q2OWQ=", + "slug": "my-registry", + "url": "http://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry", + "web_url": "http://buildkite.com/organizations/buildkite/my_great_org/registries/my-registry" + } + } +] +``` + +Required scope: `read_registries` + +Success response: `200 OK` From 9f56cdf8e63e562354145ea3602707acd04ee8fb Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Wed, 11 Sep 2024 19:43:25 +0900 Subject: [PATCH 090/369] api docs for registry tokens show --- .../apis/rest_api/packages/registry_tokens.md | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/pages/apis/rest_api/packages/registry_tokens.md b/pages/apis/rest_api/packages/registry_tokens.md index eb9047c82a..4616e8fd0f 100644 --- a/pages/apis/rest_api/packages/registry_tokens.md +++ b/pages/apis/rest_api/packages/registry_tokens.md @@ -8,7 +8,7 @@ Returns a list of a registry's tokens. ```bash curl -H "Authorization: Bearer $TOKEN" \ - -X POST "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}/tokens" \ + -X GET "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}/tokens" \ -H "Content-Type: application/json" ``` @@ -48,3 +48,47 @@ curl -H "Authorization: Bearer $TOKEN" \ Required scope: `read_registries` Success response: `200 OK` + +## Get a registry token + +Returns the details for a single registry token. + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X GET "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}/tokens/#{id}" +``` + +```json +{ + "id": "0191b6a2-aa51-70d0-8a5f-aabce115b0fd", + "graphql_id": "UmVnaXN0cnlUb2tlbi0tLTAxOTFiNmEyLWFhNTEtNzBkMC04YTVmLWFhYmNlMTE1YjBmZA==", + "description": "Usher", + "url": "http://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry/tokens/0191b6a2-aa51-70d0-8a5f-aabce115b0fd", + "created_at": "2024-09-03T06:46:39.441Z", + "created_by": { + "id": "0191b13b-0eb6-470d-a4c0-2085974f3580", + "graphql_id": "VXNlci0tLTAxOTFiMTNiLTBlYjYtNDcwZC1hNGMwLTIwODU5NzRmMzU4MA==", + "name": "Eminem", + "email": "eminem@buildkite.com", + "avatar_url": null, + "created_at": "2024-09-02T05:35:23.318Z" + }, + "organization": { + "id": "018a456f-e581-44b6-c5a4-1d8a5f7094ee", + "slug": "my_great_org", + "url": "https://api.buildkite.com/v2/analytics/organizations/my_great_org", + "web_url": "https://buildkite.com/organizations/my_great_org" + }, + "registry": { + "id": "018f56ef-9ef4-70f0-aba2-0f4578e3d69d", + "graphql_id": "UmVnaXN0cnktLS0wMThmNTZlZi05ZWY0LTcwZjAtYWJhMi0wZjQ1NzhlM2Q2OWQ=", + "slug": "my-registry", + "url": "http://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry", + "web_url": "http://buildkite.com/organizations/buildkite/my_great_org/registries/my-registry" + } +} +``` + +Required scope: `read_registries` + +Success response: `200 OK` From ed25663d610eadc14798680aa64335cb915a552b Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Thu, 12 Sep 2024 00:11:36 +0900 Subject: [PATCH 091/369] api docs for registry tokens update --- .../apis/rest_api/packages/registry_tokens.md | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/pages/apis/rest_api/packages/registry_tokens.md b/pages/apis/rest_api/packages/registry_tokens.md index 4616e8fd0f..d156d66857 100644 --- a/pages/apis/rest_api/packages/registry_tokens.md +++ b/pages/apis/rest_api/packages/registry_tokens.md @@ -92,3 +92,57 @@ curl -H "Authorization: Bearer $TOKEN" \ Required scope: `read_registries` Success response: `200 OK` + +## Update a registry token + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X POST "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}/tokens/#{id}" \ + -H "Content-Type: application/json" \ + -d '{ + "description": "Usher" + }' +``` + +```json +{ + "id": "0191b6a2-aa51-70d0-8a5f-aabce115b0fd", + "graphql_id": "UmVnaXN0cnlUb2tlbi0tLTAxOTFiNmEyLWFhNTEtNzBkMC04YTVmLWFhYmNlMTE1YjBmZA==", + "description": "Usher", + "url": "http://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry/tokens/0191b6a2-aa51-70d0-8a5f-aabce115b0fd", + "created_at": "2024-09-03T06:46:39.441Z", + "created_by": { + "id": "0191b13b-0eb6-470d-a4c0-2085974f3580", + "graphql_id": "VXNlci0tLTAxOTFiMTNiLTBlYjYtNDcwZC1hNGMwLTIwODU5NzRmMzU4MA==", + "name": "Eminem", + "email": "eminem@buildkite.com", + "avatar_url": null, + "created_at": "2024-09-02T05:35:23.318Z" + }, + "organization": { + "id": "018a456f-e581-44b6-c5a4-1d8a5f7094ee", + "slug": "my_great_org", + "url": "https://api.buildkite.com/v2/analytics/organizations/my_great_org", + "web_url": "https://buildkite.com/organizations/my_great_org" + }, + "registry": { + "id": "018f56ef-9ef4-70f0-aba2-0f4578e3d69d", + "graphql_id": "UmVnaXN0cnktLS0wMThmNTZlZi05ZWY0LTcwZjAtYWJhMi0wZjQ1NzhlM2Q2OWQ=", + "slug": "my-registry", + "url": "http://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry", + "web_url": "http://buildkite.com/organizations/buildkite/my_great_org/registries/my-registry" + } +} +``` + +Required [request body properties](/docs/api#request-body-properties): + + + + + +
descriptionDescription of the token
Example: "Usher".
+ +Required scope: `write_registries` + +Success response: `200 OK` From 9aabe727f9b3ff41e5f5f42715d9d557f09186da Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Thu, 12 Sep 2024 01:57:38 +0900 Subject: [PATCH 092/369] api docs for registry tokens delete --- pages/apis/rest_api/packages/registry_tokens.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pages/apis/rest_api/packages/registry_tokens.md b/pages/apis/rest_api/packages/registry_tokens.md index d156d66857..ac581f41c9 100644 --- a/pages/apis/rest_api/packages/registry_tokens.md +++ b/pages/apis/rest_api/packages/registry_tokens.md @@ -146,3 +146,15 @@ Required [request body properties](/docs/api#request-body-properties): Required scope: `write_registries` Success response: `200 OK` + + +## Delete a registry token + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X DELETE "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}/tokens/#{id}" +``` + +Required scope: `write_registries` + +Success response: `200 OK` From 338f8c6c18af6e91ab42972a3a00b9d20707d820 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Thu, 12 Sep 2024 02:44:35 +0900 Subject: [PATCH 093/369] api docs for registry tokens create --- .../apis/rest_api/packages/registry_tokens.md | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/pages/apis/rest_api/packages/registry_tokens.md b/pages/apis/rest_api/packages/registry_tokens.md index ac581f41c9..67c05b1f09 100644 --- a/pages/apis/rest_api/packages/registry_tokens.md +++ b/pages/apis/rest_api/packages/registry_tokens.md @@ -2,6 +2,60 @@ The registry tokens API lets you create and manage credentials needed to install and use packages in a registry. +## Create a registry token + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X POST "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}/tokens" \ + -H "Content-Type: application/json" \ + -d '{ + "description": "Usher" + }' +``` + +```json +{ + "id": "0191b6a2-aa51-70d0-8a5f-aabce115b0fd", + "graphql_id": "UmVnaXN0cnlUb2tlbi0tLTAxOTFiNmEyLWFhNTEtNzBkMC04YTVmLWFhYmNlMTE1YjBmZA==", + "description": "Usher", + "url": "http://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry/tokens/0191b6a2-aa51-70d0-8a5f-aabce115b0fd", + "created_at": "2024-09-03T06:46:39.441Z", + "created_by": { + "id": "0191b13b-0eb6-470d-a4c0-2085974f3580", + "graphql_id": "VXNlci0tLTAxOTFiMTNiLTBlYjYtNDcwZC1hNGMwLTIwODU5NzRmMzU4MA==", + "name": "Eminem", + "email": "eminem@buildkite.com", + "avatar_url": null, + "created_at": "2024-09-02T05:35:23.318Z" + }, + "organization": { + "id": "018a456f-e581-44b6-c5a4-1d8a5f7094ee", + "slug": "my_great_org", + "url": "https://api.buildkite.com/v2/analytics/organizations/my_great_org", + "web_url": "https://buildkite.com/organizations/my_great_org" + }, + "registry": { + "id": "018f56ef-9ef4-70f0-aba2-0f4578e3d69d", + "graphql_id": "UmVnaXN0cnktLS0wMThmNTZlZi05ZWY0LTcwZjAtYWJhMi0wZjQ1NzhlM2Q2OWQ=", + "slug": "my-registry", + "url": "http://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry", + "web_url": "http://buildkite.com/organizations/buildkite/my_great_org/registries/my-registry" + } +} +``` + +Required [request body properties](/docs/api#request-body-properties): + + + + + +
descriptionDescription of the token
Example: "Usher".
+ +Required scope: `write_registries` + +Success response: `200 OK` + ## List all registry tokens Returns a list of a registry's tokens. From a9c15492d72873c8e9806ee45a425fbb9fb1371e Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Thu, 12 Sep 2024 03:12:00 +0900 Subject: [PATCH 094/369] api docs for packages show --- data/nav.yml | 4 +++ pages/apis/rest_api/packages/packages.md | 38 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 pages/apis/rest_api/packages/packages.md diff --git a/data/nav.yml b/data/nav.yml index 605e8cae2e..900a002506 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -557,6 +557,10 @@ - name: "Members" path: "apis/rest-api/organizations/members" pill: "beta" + - name: "Packages" + children: + - name: "Packages" + path: "apis/rest-api/packages/packages" - name: "Pipelines " children: - name: "Overview" diff --git a/pages/apis/rest_api/packages/packages.md b/pages/apis/rest_api/packages/packages.md new file mode 100644 index 0000000000..39a9d95d34 --- /dev/null +++ b/pages/apis/rest_api/packages/packages.md @@ -0,0 +1,38 @@ +# Packages API + +The packages tokens API lets you create and manage packages in a registry. + +## Get a package + +Returns the details for a single package. + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X GET "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}/packages/#{id}" +``` + +```json +{ + "id": "0191e23a-4bc8-7683-bfa4-5f73bc9b7c44", + "url": "https://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry/packages/0191e23a-4bc8-7683-bfa4-5f73bc9b7c44", + "web_url": "https://buildkite.com/organizations/my_great_org/packages/registries/my-registry/packages/0191e23a-4bc8-7683-bfa4-5f73bc9b7c44", + "name": "banana", + "organization": { + "id": "0190e784-eeb7-4ce4-9d2d-87f7aba85433", + "slug": "my_great_org", + "url": "https://api.buildkite.com/v2/organizations/my_great_org", + "web_url": "https://buildkite.com/my_great_org" + }, + "registry": { + "id": "0191e238-e0a3-7b0b-bb34-beea0035a39d", + "graphql_id": "UmVnaXN0cnktLS0wMTkxZTIzOC1lMGEzLTdiMGItYmIzNC1iZWVhMDAzNWEzOWQ=", + "slug": "my-registry", + "url": "https://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry", + "web_url": "https://buildkite.com/organizations/my_great_org/packages/registries/my-registry" + } +} +``` + +Required scope: `read_packages` + +Success response: `200 OK` From 4bc5dfc754b3765ebe7dc9e621983dedbe2892f8 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Thu, 12 Sep 2024 03:15:36 +0900 Subject: [PATCH 095/369] api docs for packages destroy --- pages/apis/rest_api/packages/packages.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pages/apis/rest_api/packages/packages.md b/pages/apis/rest_api/packages/packages.md index 39a9d95d34..49bf31cbd9 100644 --- a/pages/apis/rest_api/packages/packages.md +++ b/pages/apis/rest_api/packages/packages.md @@ -36,3 +36,14 @@ curl -H "Authorization: Bearer $TOKEN" \ Required scope: `read_packages` Success response: `200 OK` + +## Delete a package + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X DELETE "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}/packages/#{id}" +``` + +Required scope: `delete_packages` + +Success response: `200 OK` From dd0cb89fff1cf4641c25dd3a53e33cb7f2c65159 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Thu, 12 Sep 2024 03:26:35 +0900 Subject: [PATCH 096/369] api docs for package create --- pages/apis/rest_api/packages/packages.md | 43 ++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/pages/apis/rest_api/packages/packages.md b/pages/apis/rest_api/packages/packages.md index 49bf31cbd9..d84bebfcdc 100644 --- a/pages/apis/rest_api/packages/packages.md +++ b/pages/apis/rest_api/packages/packages.md @@ -2,6 +2,49 @@ The packages tokens API lets you create and manage packages in a registry. +## Upload a package + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + -X POST "https://api.buildkite.com/v2/packages/organizations/#{org.slug}/registries/#{registry.slug}/packages" \ + -H "Content-Type: application/json" \ + -F 'file=@path/to/ruby/gem/banana-1.0.0.gem' +``` + +```json +{ + "id": "0191e23a-4bc8-7683-bfa4-5f73bc9b7c44", + "url": "https://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry/packages/0191e23a-4bc8-7683-bfa4-5f73bc9b7c44", + "web_url": "https://buildkite.com/organizations/my_great_org/packages/registries/my-registry/packages/0191e23a-4bc8-7683-bfa4-5f73bc9b7c44", + "name": "banana", + "organization": { + "id": "0190e784-eeb7-4ce4-9d2d-87f7aba85433", + "slug": "my_great_org", + "url": "https://api.buildkite.com/v2/organizations/my_great_org", + "web_url": "https://buildkite.com/my_great_org" + }, + "registry": { + "id": "0191e238-e0a3-7b0b-bb34-beea0035a39d", + "graphql_id": "UmVnaXN0cnktLS0wMTkxZTIzOC1lMGEzLTdiMGItYmIzNC1iZWVhMDAzNWEzOWQ=", + "slug": "my-registry", + "url": "https://api.buildkite.com/v2/packages/organizations/my_great_org/registries/my-registry", + "web_url": "https://buildkite.com/organizations/my_great_org/packages/registries/my-registry" + } +} +``` + +Required request form-field content: + + + + + +
filePath to the package.
Example: "file=@path/to/ruby/gem/banana-1.0.0.gem".
+ +Required scope: `create_packages` + +Success response: `200 OK` + ## Get a package Returns the details for a single package. From 4024aca863569ad06d4f781cf9959831bcce4e6c Mon Sep 17 00:00:00 2001 From: James Sammut Date: Thu, 12 Sep 2024 06:26:50 +1000 Subject: [PATCH 097/369] getRules GraphQL Rules cookbook indent fix --- pages/apis/graphql/cookbooks/rules.md | 46 +++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/pages/apis/graphql/cookbooks/rules.md b/pages/apis/graphql/cookbooks/rules.md index c809b1bf65..84c30ac043 100644 --- a/pages/apis/graphql/cookbooks/rules.md +++ b/pages/apis/graphql/cookbooks/rules.md @@ -9,36 +9,36 @@ You can test out the Buildkite GraphQL API using the [Buildkite explorer](https: Get the first 10 rules and their information for an organization. ```graphql - query getRules { - organization(slug: "organization-slug") { - rules(first: 10) { - edges { - node { - id - type - targetType - sourceType - source { - ... on Pipeline { - slug - } - } - target { - ... on Pipeline { - slug - } +query getRules { + organization(slug: "organization-slug") { + rules(first: 10) { + edges { + node { + id + type + targetType + sourceType + source { + ... on Pipeline { + slug } - effect - action - createdBy { - id - name + } + target { + ... on Pipeline { + slug } } + effect + action + createdBy { + id + name + } } } } } +} ``` ## Create a rule From 97d1e8ddd0de364a68928353425a3a5d4b12642a Mon Sep 17 00:00:00 2001 From: Giles Gas Date: Thu, 12 Sep 2024 09:07:27 +1000 Subject: [PATCH 098/369] Update pages/agent/v3/signed_pipelines.md --- pages/agent/v3/signed_pipelines.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pages/agent/v3/signed_pipelines.md b/pages/agent/v3/signed_pipelines.md index 9dc749559c..013155a01f 100644 --- a/pages/agent/v3/signed_pipelines.md +++ b/pages/agent/v3/signed_pipelines.md @@ -106,9 +106,7 @@ This ensures that whenever those agents upload steps to Buildkite, they'll gener verification-failure-behavior= ``` -This setting determines the Buildkite agent’s response when it receives a job without a proper signature. It specifies how strictly the agent should enforce signature verification for incoming jobs. The agent will warn about missing or invalid signatures, but will still proceed with executing the job. If not explicitly specified, the default behavior is `block`, which will prevent any job without a valid signature from running, ensuring a secure pipeline environment by default. - - +This setting determines the Buildkite agent's response when it receives a job without a proper signature, and also specifies how strictly the agent should enforce signature verification for incoming jobs. The agent will warn about missing or invalid signatures, but will still proceed to execute the job. If not explicitly specified, the default behavior is `block`, which prevents any job without a valid signature from running, ensuring a secure pipeline environment by default. On instances that verify jobs, add: From 106791f6d2a4629b212ebe1c99708aa2d9236837 Mon Sep 17 00:00:00 2001 From: Mark Wolfe Date: Thu, 12 Sep 2024 11:34:13 +1000 Subject: [PATCH 099/369] Add a section on using AWS KMS keys with signed pipelines --- pages/agent/v3/signed_pipelines.md | 77 ++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/pages/agent/v3/signed_pipelines.md b/pages/agent/v3/signed_pipelines.md index 013155a01f..59f9b61fb5 100644 --- a/pages/agent/v3/signed_pipelines.md +++ b/pages/agent/v3/signed_pipelines.md @@ -42,9 +42,16 @@ The following fields are included in the signature for each step: You'll need to configure your agents and update pipeline definitions to enable signed pipelines. -### Step 1: Generate a key pair +Behind the scenes, signed pipelines use [JSON Web Signing (JWS)](https://datatracker.ietf.org/doc/html/rfc7797) to generate signatures. There are two options for creation of keys used with JWS, these are: + +* Self managed key pairs +* AWS KMS managed keys -Behind the scenes, signed pipelines use [JSON Web Signing (JWS)](https://datatracker.ietf.org/doc/html/rfc7797) to generate signatures. You'll need to generate a [JSON Web Key Set (JWKS)](https://datatracker.ietf.org/doc/html/rfc7517) to sign and verify your pipelines with, then configure your agents to use those keys. +## Self Managed Key Creation + +You'll need to generate a [JSON Web Key Set (JWKS)](https://datatracker.ietf.org/doc/html/rfc7517) to sign and verify your pipelines with, then configure your agents to use those keys. + +### Step 1: Generate a key pair Luckily, the agent has you covered! A JWKS generation tool is built into the agent, which you can use to generate a key pair. To use it, you'll need to [install the agent on your machine](/docs/agent/v3/installation), and then run: @@ -143,7 +150,7 @@ Replacing the following: This will download the pipeline definition using the Buildkite GraphQL API, sign all steps, and upload the signed pipeline definition back to Buildkite. -## Rotating signing keys +### Rotating signing keys Regularly rotating signing and verification keys is good security practice, as it reduces the impact of a compromised key. Because signed pipelines use JWKS as their key format, rotating keys is easy. @@ -154,3 +161,67 @@ To rotate your keys: 1. Update the `signing-key-id` on your signing agents to use the new key ID. The verifying agents will automatically use the public key with the matching key ID, if it's present. + +## AWS KMS Managed Key Setup + +AWS Key Management Service (AWS KMS) is a web service that securely protects cryptographic keys, when using this service with signed pipelines the agent never has access to the private key used to sign pipelines, with calls going via the KMS API. + +### Step 1: Create a KMS Key + +AWS KMS has a myriad of options when creating keys, for pipeline signing we require that you use some specific settings. + +1. The key type must be Asymmetric and have a usage type of `SIGN_VERIFY`. +2. The key spec must be `ECC_NIST_P256`. + +If your using the AWS CLI the key can be created as follows: + +```bash +aws kms create-key --key-spec ECC_NIST_P256 --key-usage SIGN_VERIFY +``` + +Once created you can retrieve the key identifier, this will be a UUID, for example `1234abcd-12ab-34cd-56ef-1234567890ab`. + +Optionally you can create a key alias, or friendly name for the key as follows: + +```bash +aws kms create-alias \ + --alias-name alias/example-alias \ + --target-key-id 1234abcd-12ab-34cd-56ef-1234567890ab +``` + +### Step 2: Configure the agents + +Next, you need to configure your agents to use the KMS key you created. On agents that upload pipelines, add the following to the agent's config file: + +```ini +signing-aws-kms-key= +``` + +This ensures that whenever those agents upload steps to Buildkite, they'll generate signatures using the private key you generated earlier. It also ensures that those agents verify the signatures of any steps they run, using the public key. + +```ini +verification-failure-behavior= +``` + +This setting determines the Buildkite agent's response when it receives a job without a proper signature, and also specifies how strictly the agent should enforce signature verification for incoming jobs. The agent will warn about missing or invalid signatures, but will still proceed to execute the job. If not explicitly specified, the default behavior is `block`, which prevents any job without a valid signature from running, ensuring a secure pipeline environment by default. + +### Step 3: Sign all steps + +To sign steps configured in the Buildkite dashboard, you need to add static signatures to the YAML. To do this, run: + +```sh +buildkite-agent tool sign \ + --graphql-token \ + --signing-aws-kms-key \ + --organization-slug \ + --pipeline-slug \ + --update +``` + +Replacing the following: + +- `` with a Buildkite GraphQL token that has the `write_pipelines` scope. +- `` with the path to the private key set you generated earlier. +- `` with the AWS KMS key ID or alias created earlier. +- `` with the slug of the organization the pipeline is in. +- `` with the slug of the pipeline you want to sign. From 37bec34d111bef827116718355fee76ce418dcee Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 12 Sep 2024 11:44:57 +1000 Subject: [PATCH 100/369] Fix highlighting issue. --- data/nav.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/data/nav.yml b/data/nav.yml index 0f75cd6020..f3a7c62f58 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -557,11 +557,15 @@ - name: "Members" path: "apis/rest-api/organizations/members" pill: "beta" - - name: "Packages" + - name: "Packages " + # Keep space at end to prevent "Packages" in global nav bar being + # highlighted when any child page of "API > REST > Packages" is selected. children: - name: "Registries" path: "apis/rest-api/packages/registries" - - name: "Pipelines" + - name: "Pipelines " + # Keep space at end to prevent "Pipelines" in global nav bar being + # highlighted when any child page of "API > REST > Pipelines" is selected. children: - name: "Overview" path: "apis/rest-api/pipelines" From 66688f7a68fdf6562dee920a8d116a462ad6e4f6 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 12 Sep 2024 12:09:47 +1000 Subject: [PATCH 101/369] Add term 'endpoint' to relevant sections throughout the REST API docs for consistency. Implement a few other format tidying and improvements. --- pages/apis/agent_api.md | 10 +++++----- pages/apis/agent_api/metrics.md | 2 +- pages/apis/rest_api/access_token.md | 4 ++-- pages/apis/rest_api/agents.md | 1 - pages/apis/rest_api/analytics/flaky_tests.md | 2 +- pages/apis/rest_api/clusters.md | 2 +- pages/apis/rest_api/meta.md | 2 +- pages/apis/rest_api/organizations.md | 2 +- pages/apis/rest_api/organizations/members.md | 2 +- pages/apis/rest_api/packages/registries.md | 2 +- pages/apis/rest_api/pipeline_templates.md | 2 +- pages/apis/rest_api/pipelines.md | 2 +- pages/apis/rest_api/rules.md | 2 +- pages/apis/rest_api/teams.md | 2 +- pages/apis/rest_api/teams/members.md | 2 +- pages/apis/rest_api/teams/pipelines.md | 2 +- pages/apis/rest_api/teams/suites.md | 2 +- pages/apis/rest_api/user.md | 2 +- 18 files changed, 22 insertions(+), 23 deletions(-) diff --git a/pages/apis/agent_api.md b/pages/apis/agent_api.md index 698a242a94..7595ef4f4d 100644 --- a/pages/apis/agent_api.md +++ b/pages/apis/agent_api.md @@ -1,12 +1,12 @@ # Agent REST API overview -The Agent REST API is used for agent registration, agent deregistration, starting jobs on agents, finishing jobs on agents, and agent metrics. +The agent REST API endpoint is used for agent registration, agent deregistration, starting jobs on agents, finishing jobs on agents, and agent metrics. The only publicly available endpoint is `/metrics`. The [Buildkite metrics agent](https://github.com/buildkite/buildkite-agent-metrics) uses the data returned by the metrics endpoint for agent autoscaling. -All other endpoints in the Agent API are intended only for use by the Buildkite Agent, therefore stability and backwards compatibility are not guaranteed, and changes won't be announced. +All other endpoints in the agent API are intended only for use by the Buildkite Agent, therefore stability and backwards compatibility are not guaranteed, and changes won't be announced. -The current version of the Agent API is v3. +The current version of the agent API is v3. ## Schema @@ -24,9 +24,9 @@ curl https://agent.buildkite.com ## Authentication -Unlike the [Buildkite REST API](/docs/apis/rest-api), which uses an [API access token](/docs/apis/rest-api#authentication), the Agent REST API uses an [Agent registration token](/docs/agent/v3/tokens) for authentication. +Unlike the [Buildkite REST API](/docs/apis/rest-api), which uses an [API access token](/docs/apis/rest-api#authentication), the agent REST API uses an [agent token](/docs/agent/v3/tokens) for authentication. -To authenticate using an Agent registration token, set the `Authorization` HTTP header to the word `Token`, followed by a space, followed by the access token. For example: +To authenticate using an agent token, set the `Authorization` HTTP header to the word `Token`, followed by a space, followed by the agent token. For example: ```bash curl -H "Authorization: Token $TOKEN" https://agent.buildkite.com/v3/metrics diff --git a/pages/apis/agent_api/metrics.md b/pages/apis/agent_api/metrics.md index 4135bb484a..27a964f641 100644 --- a/pages/apis/agent_api/metrics.md +++ b/pages/apis/agent_api/metrics.md @@ -4,7 +4,7 @@ toc: false # Metrics API -The Metrics API endpoint provides information on idle and busy agents, jobs, and queues for the [Agent registration token](/docs/agent/v3/tokens) supplied in the request `Authorization` header. +The metrics API endpoint provides information on idle and busy agents, jobs, and queues for the [Agent registration token](/docs/agent/v3/tokens) supplied in the request `Authorization` header. ## Get metrics diff --git a/pages/apis/rest_api/access_token.md b/pages/apis/rest_api/access_token.md index e528b98c3a..8c8ed89b62 100644 --- a/pages/apis/rest_api/access_token.md +++ b/pages/apis/rest_api/access_token.md @@ -1,8 +1,8 @@ # Access token API -The Access token API allows you to inspect and revoke an API access token. This can be useful if you find a token, can't identify its owner, and you want to revoke it. +The access token API endpoint allows you to inspect and revoke an API access token. This can be useful if you find a token, can't identify its owner, and you want to revoke it. ->📘 +> 📘 > All the endpoints expect the token to be provided using the
Authorization HTTP header. diff --git a/pages/apis/rest_api/agents.md b/pages/apis/rest_api/agents.md index d371ce8786..e1508d9dba 100644 --- a/pages/apis/rest_api/agents.md +++ b/pages/apis/rest_api/agents.md @@ -1,6 +1,5 @@ # Agents API - ## List agents Returns a [paginated list](<%= paginated_resource_docs_url %>) of an organization's agents. The list only includes connected agents - agents in a disconnected state are not returned. diff --git a/pages/apis/rest_api/analytics/flaky_tests.md b/pages/apis/rest_api/analytics/flaky_tests.md index 99154ced6b..f5de36c0b7 100644 --- a/pages/apis/rest_api/analytics/flaky_tests.md +++ b/pages/apis/rest_api/analytics/flaky_tests.md @@ -1,6 +1,6 @@ # Flaky tests API -The Flaky test API endpoint provides information about tests detected as flaky in a test suite. +The flaky test API endpoint provides information about tests detected as flaky in a test suite. ## List all flaky tests diff --git a/pages/apis/rest_api/clusters.md b/pages/apis/rest_api/clusters.md index 33bb0f3905..27cffb89f0 100644 --- a/pages/apis/rest_api/clusters.md +++ b/pages/apis/rest_api/clusters.md @@ -1,6 +1,6 @@ # Clusters API -The clusters API lets you create and manage clusters in your organization. +The clusters API endpoint lets you create and manage clusters in your organization. ## Clusters diff --git a/pages/apis/rest_api/meta.md b/pages/apis/rest_api/meta.md index 0db9a8ff15..492f72bfdc 100644 --- a/pages/apis/rest_api/meta.md +++ b/pages/apis/rest_api/meta.md @@ -1,6 +1,6 @@ # Meta API -The Meta API provides information about Buildkite, including a list of Buildkite's IP addresses. +The meta API endpoint provides information about Buildkite, including a list of Buildkite's IP addresses. It does not require authentication. diff --git a/pages/apis/rest_api/organizations.md b/pages/apis/rest_api/organizations.md index e71dd7ef93..a74b16abfb 100644 --- a/pages/apis/rest_api/organizations.md +++ b/pages/apis/rest_api/organizations.md @@ -1,6 +1,6 @@ # Organizations API -The organizations API: +The organizations API endpoint: - allows you to list organizations and retrieve information about an organization diff --git a/pages/apis/rest_api/organizations/members.md b/pages/apis/rest_api/organizations/members.md index 9e5dfae107..f05b73f6d3 100644 --- a/pages/apis/rest_api/organizations/members.md +++ b/pages/apis/rest_api/organizations/members.md @@ -1,6 +1,6 @@ # Organization members API -The organization members API allows users to view all members of an organization. +The organization members API endpoint allows users to view all members of an organization. ## Organization member data model diff --git a/pages/apis/rest_api/packages/registries.md b/pages/apis/rest_api/packages/registries.md index 33d84ee184..85de10f1ba 100644 --- a/pages/apis/rest_api/packages/registries.md +++ b/pages/apis/rest_api/packages/registries.md @@ -1,6 +1,6 @@ # Registries API -The registries API lets you create and manage registries in your organization. +The registries API endpoint lets you [create and manage registries](/docs/packages/manage-registries) in your organization. ## List all registries diff --git a/pages/apis/rest_api/pipeline_templates.md b/pages/apis/rest_api/pipeline_templates.md index 052b055367..abf2cf1b19 100644 --- a/pages/apis/rest_api/pipeline_templates.md +++ b/pages/apis/rest_api/pipeline_templates.md @@ -3,7 +3,7 @@ > 📘 Enterprise feature > [Pipeline templates](https://buildkite.com/docs/pipelines/templates) are only available on an [Enterprise](https://buildkite.com/pricing) plan. -The pipeline templates API allows admins to create and manage pipeline templates for an organization. +The pipeline templates API endpoint allows admins to create and manage pipeline templates for an organization. Non-admins can only read or assign pipeline templates marked as `available` by organization admins. ## Pipeline template data model diff --git a/pages/apis/rest_api/pipelines.md b/pages/apis/rest_api/pipelines.md index 1a0c04024c..5bfa3f2303 100644 --- a/pages/apis/rest_api/pipelines.md +++ b/pages/apis/rest_api/pipelines.md @@ -1,6 +1,6 @@ # Pipelines API -The pipelines API consists of several endpoints that allow you to manage: +The pipelines API endpoint consists of several endpoints that allow you to manage: - pipelines, along with their [builds](/docs/apis/rest-api/builds) - a build's [annotations](/docs/apis/rest-api/annotations), [artifacts](/docs/apis/rest-api/artifacts), and [jobs](/docs/apis/rest-api/jobs) diff --git a/pages/apis/rest_api/rules.md b/pages/apis/rest_api/rules.md index c2eed2e7c3..1395469ff0 100644 --- a/pages/apis/rest_api/rules.md +++ b/pages/apis/rest_api/rules.md @@ -1,6 +1,6 @@ # Rules API -The rules API lets you create and manage rules in your organization. +The rules API endpoint lets you create and manage rules in your organization. ## Rules diff --git a/pages/apis/rest_api/teams.md b/pages/apis/rest_api/teams.md index 647501f212..ee2e5c961d 100644 --- a/pages/apis/rest_api/teams.md +++ b/pages/apis/rest_api/teams.md @@ -1,6 +1,6 @@ # Teams API -The teams API allows you to view and manage teams within an organization. +The teams API endpoint allows you to view and manage teams within an organization. ## Team data model diff --git a/pages/apis/rest_api/teams/members.md b/pages/apis/rest_api/teams/members.md index 13cd1cda02..c1cc514c9f 100644 --- a/pages/apis/rest_api/teams/members.md +++ b/pages/apis/rest_api/teams/members.md @@ -1,6 +1,6 @@ # Team members API -The team members API allows users to review, create, update, and delete members associated with a team in your organization. +The team members API endpoint allows users to review, create, update, and delete members associated with a team in your organization. ## Team member data model diff --git a/pages/apis/rest_api/teams/pipelines.md b/pages/apis/rest_api/teams/pipelines.md index 34634081c9..956cce4495 100644 --- a/pages/apis/rest_api/teams/pipelines.md +++ b/pages/apis/rest_api/teams/pipelines.md @@ -1,6 +1,6 @@ # Team pipelines API -The team pipelines API allows users to review, create, update, and delete pipelines associated with a team in your organization. +The team pipelines API endpoint allows users to review, create, update, and delete pipelines associated with a team in your organization. ## Team pipeline data model diff --git a/pages/apis/rest_api/teams/suites.md b/pages/apis/rest_api/teams/suites.md index 786d825ef4..f0e447cc23 100644 --- a/pages/apis/rest_api/teams/suites.md +++ b/pages/apis/rest_api/teams/suites.md @@ -1,6 +1,6 @@ # Team suites API -The team suites API allows users to review, create, update, and delete test suites associated with a team in your organization. +The team suites API endpoint allows users to review, create, update, and delete test suites associated with a team in your organization. ## Team suite data model diff --git a/pages/apis/rest_api/user.md b/pages/apis/rest_api/user.md index 9a34111aee..6c303e859c 100644 --- a/pages/apis/rest_api/user.md +++ b/pages/apis/rest_api/user.md @@ -1,6 +1,6 @@ # User API -The User API endpoint allows you to inspect details about the user account that owns the API token that is currently being used. +The user API endpoint allows you to inspect details about the user account that owns the API token that is currently being used. ## Get the current user From 8ee56ea9ee029db1d390aef12c2cee169725e314 Mon Sep 17 00:00:00 2001 From: Mark Wolfe Date: Thu, 12 Sep 2024 12:17:59 +1000 Subject: [PATCH 102/369] Add a section on IAM actions required to perform different roles --- pages/agent/v3/signed_pipelines.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pages/agent/v3/signed_pipelines.md b/pages/agent/v3/signed_pipelines.md index 59f9b61fb5..785f5ed454 100644 --- a/pages/agent/v3/signed_pipelines.md +++ b/pages/agent/v3/signed_pipelines.md @@ -225,3 +225,18 @@ Replacing the following: - `` with the AWS KMS key ID or alias created earlier. - `` with the slug of the organization the pipeline is in. - `` with the slug of the pipeline you want to sign. + +### Step 4: Assign IAM Permissions to your Agents + +There are two common roles for agents when using signed pipelines, these being those that sign and upload pipelines, and those that verify steps. To follow least privilege best practice you should access to the KMS key using IAM to specific actions as seen below. + +For agents which will sign and verify pipelines the following IAM Actions are required. + +- kms:Sign +- kms:Verify +- kms:GetPublicKey + +For agents which only verify pipelines the following IAM Actions are required. + +- kms:Verify +- kms:GetPublicKey From 5e4f2b5bd080b9d0f2750c4ad62566dab5df0962 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Thu, 12 Sep 2024 12:11:14 +0900 Subject: [PATCH 103/369] fix scope naming mistake --- pages/apis/rest_api/packages/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/apis/rest_api/packages/registries.md b/pages/apis/rest_api/packages/registries.md index c2ab576c01..265a368004 100644 --- a/pages/apis/rest_api/packages/registries.md +++ b/pages/apis/rest_api/packages/registries.md @@ -48,7 +48,7 @@ Optional [request body properties](/docs/api#request-body-properties): -Required scope: `create_registries` +Required scope: `write_registries` Success response: `200 OK` From ee2b1cac9543649624115d798f933325263c11a4 Mon Sep 17 00:00:00 2001 From: Mark Wolfe Date: Thu, 12 Sep 2024 13:13:26 +1000 Subject: [PATCH 104/369] Fixes for linting errors/warnings --- pages/agent/v3/signed_pipelines.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pages/agent/v3/signed_pipelines.md b/pages/agent/v3/signed_pipelines.md index 785f5ed454..6e32bbb9fb 100644 --- a/pages/agent/v3/signed_pipelines.md +++ b/pages/agent/v3/signed_pipelines.md @@ -162,11 +162,11 @@ To rotate your keys: The verifying agents will automatically use the public key with the matching key ID, if it's present. -## AWS KMS Managed Key Setup +## AWS KMS managed key setup -AWS Key Management Service (AWS KMS) is a web service that securely protects cryptographic keys, when using this service with signed pipelines the agent never has access to the private key used to sign pipelines, with calls going via the KMS API. +AWS Key Management Service (AWS KMS) is a web service that securely protects cryptographic keys, when using this service with signed pipelines the agent never has access to the private key used to sign pipelines, with calls going with the KMS API. -### Step 1: Create a KMS Key +### Step 1: Create a KMS key AWS KMS has a myriad of options when creating keys, for pipeline signing we require that you use some specific settings. @@ -226,7 +226,7 @@ Replacing the following: - `` with the slug of the organization the pipeline is in. - `` with the slug of the pipeline you want to sign. -### Step 4: Assign IAM Permissions to your Agents +### Step 4: Assign IAM permissions to your agents There are two common roles for agents when using signed pipelines, these being those that sign and upload pipelines, and those that verify steps. To follow least privilege best practice you should access to the KMS key using IAM to specific actions as seen below. From 295d3342fc39bc2085c9be3b3ef4d8b0b31e3d44 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Thu, 12 Sep 2024 12:55:14 +0900 Subject: [PATCH 105/369] reword upload to publish --- pages/apis/rest_api/packages/packages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/apis/rest_api/packages/packages.md b/pages/apis/rest_api/packages/packages.md index d84bebfcdc..eef01cb25f 100644 --- a/pages/apis/rest_api/packages/packages.md +++ b/pages/apis/rest_api/packages/packages.md @@ -2,7 +2,7 @@ The packages tokens API lets you create and manage packages in a registry. -## Upload a package +## Publish a package ```bash curl -H "Authorization: Bearer $TOKEN" \ From 24d4bda7aed7bf5dd26a72999c523c24988e39c0 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 12 Sep 2024 15:04:11 +1000 Subject: [PATCH 106/369] Fix nav. --- data/nav.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/data/nav.yml b/data/nav.yml index 1e0af20a81..f3a7c62f58 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -557,19 +557,12 @@ - name: "Members" path: "apis/rest-api/organizations/members" pill: "beta" -<<<<<<< HEAD - - name: "Packages" - children: - - name: "Packages" - path: "apis/rest-api/packages/packages" -======= - name: "Packages " # Keep space at end to prevent "Packages" in global nav bar being # highlighted when any child page of "API > REST > Packages" is selected. children: - name: "Registries" path: "apis/rest-api/packages/registries" ->>>>>>> docs-public/main - name: "Pipelines " # Keep space at end to prevent "Pipelines" in global nav bar being # highlighted when any child page of "API > REST > Pipelines" is selected. From 4e017fd740e17c4b512b7c9b6be85041ca1a8a12 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 12 Sep 2024 15:23:24 +1000 Subject: [PATCH 107/369] Fix heading in line with style guide and to please linter, as well as add word 'endpoint' for consistency across REST API docs. --- pages/apis/rest_api/packages/registry_tokens.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/apis/rest_api/packages/registry_tokens.md b/pages/apis/rest_api/packages/registry_tokens.md index e8a1ce7965..be8bf0e9a9 100644 --- a/pages/apis/rest_api/packages/registry_tokens.md +++ b/pages/apis/rest_api/packages/registry_tokens.md @@ -1,6 +1,6 @@ -# Registry Tokens API +# Registry tokens API -The registry tokens API lets you create and manage credentials needed to install and use packages in a registry. +The registry tokens API endpoint lets you create and manage credentials needed to install and use packages in a registry. ## List all registry tokens From b8e7ba5e1fef8f04372544a49ce4955d358b235d Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:41:00 +0900 Subject: [PATCH 108/369] add Packages child to nav --- data/nav.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/nav.yml b/data/nav.yml index f3a7c62f58..715f996339 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -563,6 +563,8 @@ children: - name: "Registries" path: "apis/rest-api/packages/registries" + - name: "Packages" + path: "apis/rest-api/packages/packages" - name: "Pipelines " # Keep space at end to prevent "Pipelines" in global nav bar being # highlighted when any child page of "API > REST > Pipelines" is selected. From 35b26da8e3caa6a9c27327bfd85431bd003d6d93 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Thu, 12 Sep 2024 16:02:29 +1000 Subject: [PATCH 109/369] Fix nav heading case. --- data/nav.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/nav.yml b/data/nav.yml index e7e77dcc29..b208898273 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -563,7 +563,7 @@ children: - name: "Registries" path: "apis/rest-api/packages/registries" - - name: "Registry Tokens" + - name: "Registry tokens" path: "apis/rest-api/packages/registry-tokens" - name: "Pipelines " # Keep space at end to prevent "Pipelines" in global nav bar being From a3d40222de970d21a50ea05da0c8496bf3f5b009 Mon Sep 17 00:00:00 2001 From: Elizabeth Vo <65384387+ensvo@users.noreply.github.com> Date: Fri, 13 Sep 2024 08:56:06 +0900 Subject: [PATCH 110/369] Fix typo for packages API endpoint Co-authored-by: Giles Gas --- pages/apis/rest_api/packages/packages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/apis/rest_api/packages/packages.md b/pages/apis/rest_api/packages/packages.md index eef01cb25f..f64a29cab2 100644 --- a/pages/apis/rest_api/packages/packages.md +++ b/pages/apis/rest_api/packages/packages.md @@ -1,6 +1,6 @@ # Packages API -The packages tokens API lets you create and manage packages in a registry. +The packages API endpoint lets you create and manage packages in a registry. ## Publish a package From 99ab986d49742589399b79e9dc65b7c7574e7821 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 13 Sep 2024 09:57:44 +1000 Subject: [PATCH 111/369] Fix Markdown linting issues. --- pages/agent/v3/signed_pipelines.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/agent/v3/signed_pipelines.md b/pages/agent/v3/signed_pipelines.md index 6e32bbb9fb..d00f2d3469 100644 --- a/pages/agent/v3/signed_pipelines.md +++ b/pages/agent/v3/signed_pipelines.md @@ -44,8 +44,8 @@ You'll need to configure your agents and update pipeline definitions to enable s Behind the scenes, signed pipelines use [JSON Web Signing (JWS)](https://datatracker.ietf.org/doc/html/rfc7797) to generate signatures. There are two options for creation of keys used with JWS, these are: -* Self managed key pairs -* AWS KMS managed keys +- Self managed key pairs +- AWS KMS managed keys ## Self Managed Key Creation From 6f230a942a66f87a72aaa38f5bfc333da97a121a Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 13 Sep 2024 10:00:28 +1000 Subject: [PATCH 112/369] Fix text linting issues. --- pages/agent/v3/signed_pipelines.md | 2 +- vale/styles/Buildkite/h1-h6_sentence_case.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pages/agent/v3/signed_pipelines.md b/pages/agent/v3/signed_pipelines.md index d00f2d3469..0a8e31e0d6 100644 --- a/pages/agent/v3/signed_pipelines.md +++ b/pages/agent/v3/signed_pipelines.md @@ -47,7 +47,7 @@ Behind the scenes, signed pipelines use [JSON Web Signing (JWS)](https://datatra - Self managed key pairs - AWS KMS managed keys -## Self Managed Key Creation +## Self-managed key creation You'll need to generate a [JSON Web Key Set (JWKS)](https://datatracker.ietf.org/doc/html/rfc7517) to sign and verify your pipelines with, then configure your agents to use those keys. diff --git a/vale/styles/Buildkite/h1-h6_sentence_case.yml b/vale/styles/Buildkite/h1-h6_sentence_case.yml index 7b5397abe1..97eff668e4 100644 --- a/vale/styles/Buildkite/h1-h6_sentence_case.yml +++ b/vale/styles/Buildkite/h1-h6_sentence_case.yml @@ -112,6 +112,7 @@ exceptions: - Jenkins - JSON - JUnit + - KMS - Kubernetes - Linux - M1 From 7625927fc2717f1bf530fa0afe6a17f2dcc2a8f3 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Fri, 13 Sep 2024 10:03:04 +1000 Subject: [PATCH 113/369] Fix broken link (muffet) issue. --- pages/agent/v3/signed_pipelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/agent/v3/signed_pipelines.md b/pages/agent/v3/signed_pipelines.md index 0a8e31e0d6..920a966097 100644 --- a/pages/agent/v3/signed_pipelines.md +++ b/pages/agent/v3/signed_pipelines.md @@ -156,7 +156,7 @@ Regularly rotating signing and verification keys is good security practice, as i To rotate your keys: -1. [Generate a new key pair](#enabling-signed-pipelines-on-your-agents-step-1-generate-a-key-pair). +1. [Generate a new key pair](#self-managed-key-creation-step-1-generate-a-key-pair). 1. Add the new keys to your existing key sets. Be careful not to mix public and private keys. 1. Update the `signing-key-id` on your signing agents to use the new key ID. From 6f431624b777bb2c2f63ad2e361c37cc15ae7364 Mon Sep 17 00:00:00 2001 From: Mark Wolfe Date: Fri, 13 Sep 2024 12:37:46 +1000 Subject: [PATCH 114/369] update the aws elastic ci stack to v6.27.0 --- data/content/aws-stack.yml | 160 ++++++++++++++++++++++++++++++------- 1 file changed, 133 insertions(+), 27 deletions(-) diff --git a/data/content/aws-stack.yml b/data/content/aws-stack.yml index 808994e003..c24e8887a6 100644 --- a/data/content/aws-stack.yml +++ b/data/content/aws-stack.yml @@ -1,6 +1,6 @@ --- AWSTemplateFormatVersion: "2010-09-09" -Description: "Buildkite stack v6.23.0" +Description: "Buildkite stack v6.27.0" # The Buildkite Elastic CI Stack for AWS gives you a private, # autoscaling Buildkite Agent cluster. Use it to parallelize @@ -27,15 +27,23 @@ Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: - default: Buildkite Configuration + default: Base Configuration Parameters: + - BuildkiteAgentToken - BuildkiteAgentTokenParameterStorePath - BuildkiteAgentTokenParameterStoreKMSKey - - BuildkiteAgentToken - BuildkiteQueue - Label: - default: Advanced Buildkite Configuration + default: Signed Pipelines Configuration + Parameters: + - PipelineSigningKMSKeyId + - PipelineSigningKMSKeySpec + - PipelineSigningKMSAccess + - PipelineSigningVerificationFailureBehavior + + - Label: + default: Advanced Configuration Parameters: - BuildkiteAgentRelease - BuildkiteAgentTags @@ -386,7 +394,7 @@ Parameters: Default: 125 RootVolumeIops: - Description: If the `RootVolumeType` is io1 or io2, the number of IOPS to provision for the root volume + Description: If the `RootVolumeType` is gp3, io1, or io2, the number of IOPS to provision for the root volume Type: Number Default: 1000 @@ -568,6 +576,35 @@ Parameters: Description: Optional - Customise the EC2 instance Name tag Default: "" + PipelineSigningKMSKeyId: + Type: String + Description: Optional - Identifier of the KMS key used to sign and verify pipelines (Created if left blank and PipelineSigningKMSKeySpec is selected) + Default: "" + + PipelineSigningKMSKeySpec: + Type: String + Description: The key spec for the KMS key used to sign and verify pipelines + AllowedValues: + - "ECC_NIST_P256" + - "none" + Default: "none" + + PipelineSigningKMSAccess: + Type: String + Description: The access level for the KMS key used to sign and verify pipelines + AllowedValues: + - "sign-and-verify" + - "verify" + Default: "sign-and-verify" + + PipelineSigningVerificationFailureBehavior: + Type: String + Description: The behavior when a job is received without a valid verifiable signature (without a signature, with an invalid signature, or with a signature that fails verification) + AllowedValues: + - "block" + - "warn" + Default: "block" + Rules: HasToken: Assertions: @@ -582,6 +619,17 @@ Rules: - !Ref BuildkiteAgentTokenParameterStorePath - "" AssertDescription: "You must provide BuildkiteAgentToken or BuildkiteAgentTokenParameterStorePath" + HasPipelineSigningKMSKey: + Assertions: + - Assert: + !Or + - !Equals + - !Ref PipelineSigningKMSKeyId + - "" + - !Equals + - !Ref PipelineSigningKMSKeySpec + - "none" + AssertDescription: "You must provide either provide a PipelineSigningKMSKeyId or select a PipelineSigningKMSKeySpec but not both" Outputs: VpcId: @@ -602,6 +650,12 @@ Outputs: Export: Name: !Sub '${AWS::StackName}-ManagedSecretsLoggingBucket' + PipelineSigningKMSKey: + Value: + !If [ CreatePipelineSigningKMSKey, !Ref PipelineSigningKMSKey, "none" ] + Export: + Name: !Sub '${AWS::StackName}-PipelineSigningKMSKey' + AutoScalingGroupName: Value: !Ref AgentAutoScaleGroup Export: @@ -685,6 +739,20 @@ Conditions: UseCostAllocationTags: !Equals [ !Ref EnableCostAllocationTags, "true" ] + + UsePipelineSigningKMSKey: + !Not [ !Equals [ !Ref PipelineSigningKMSKeyId, "" ] ] + + CreatePipelineSigningKMSKey: + !And + - !Equals [ !Ref PipelineSigningKMSKeyId, "" ] + - !Not [ !Equals [ !Ref PipelineSigningKMSKeySpec, "none" ] ] + + HasPipelineSigningKMSKey: + !Or [ !Condition CreatePipelineSigningKMSKey, !Condition UsePipelineSigningKMSKey ] + + HasSigningKMSAccessSignAndVerify: + !Equals [ !Ref PipelineSigningKMSAccess, "sign-and-verify" ] HasKeyName: !Not [ !Equals [ !Ref KeyName, "" ] ] @@ -760,26 +828,26 @@ Mappings: # Generated from Makefile via build/mappings.yml AWSRegion2AMI: - us-east-1 : { linuxamd64: ami-09dce4453e68fc5cd, linuxarm64: ami-0100de0b1e920f43a, windows: ami-0461661ce536ba218 } - us-east-2 : { linuxamd64: ami-01cde7aaf362f4aae, linuxarm64: ami-0ab8f88ac8c2c8d30, windows: ami-0b7b91b74290a35e1 } - us-west-1 : { linuxamd64: ami-045c6cf6c0dd4a9e4, linuxarm64: ami-0d5aec634b234e2a2, windows: ami-034824341c9421171 } - us-west-2 : { linuxamd64: ami-09132553fcbda5aee, linuxarm64: ami-0fd1d63fc28576e60, windows: ami-0116268efe38d10ea } - af-south-1 : { linuxamd64: ami-09c9633cb3f5e6fc3, linuxarm64: ami-09ba6dd6ae16f3d50, windows: ami-0fb7a12b133324fe7 } - ap-east-1 : { linuxamd64: ami-02a5f01ef4759b1c8, linuxarm64: ami-0deee5536e9c6a921, windows: ami-0c0a16b6ab6ba6660 } - ap-south-1 : { linuxamd64: ami-0217aeaac4339e394, linuxarm64: ami-06f31d79b57c0dbbf, windows: ami-0d10933d7e9a73e9e } - ap-northeast-2 : { linuxamd64: ami-092d6af0904c034b4, linuxarm64: ami-04329b443681048cd, windows: ami-0d9e4a96c235911de } - ap-northeast-1 : { linuxamd64: ami-0593f6abedb12612b, linuxarm64: ami-019d0ac19de3be566, windows: ami-02e7907798fd7f610 } - ap-southeast-2 : { linuxamd64: ami-0cc25f9f626518d8f, linuxarm64: ami-0e45dfa046084a2d3, windows: ami-0170122288687202b } - ap-southeast-1 : { linuxamd64: ami-03de8f54bc57a1397, linuxarm64: ami-046f6c2468548ca1a, windows: ami-0320407754d0bc85c } - ca-central-1 : { linuxamd64: ami-066d74f4d940d276e, linuxarm64: ami-09ef4b6d5cdb0c2e9, windows: ami-0570c4a6bb33ba9d4 } - eu-central-1 : { linuxamd64: ami-09e9769fb4085b24f, linuxarm64: ami-00c97f86e923e1020, windows: ami-08f61f9105d9e8a58 } - eu-west-1 : { linuxamd64: ami-05e7c8d4ade2095f3, linuxarm64: ami-07dd06558ae7a536d, windows: ami-0c90c3038b518ac09 } - eu-west-2 : { linuxamd64: ami-03ec96deaf3c2f04b, linuxarm64: ami-093477938aa7559d8, windows: ami-0d26fa7d30160237a } - eu-south-1 : { linuxamd64: ami-05cb67bc084762468, linuxarm64: ami-0e815380647635c63, windows: ami-0cee7ea24afffe195 } - eu-west-3 : { linuxamd64: ami-01700752047bdb1b2, linuxarm64: ami-046d7376033a1af0e, windows: ami-0358e8f0b406f3442 } - eu-north-1 : { linuxamd64: ami-0d62d22eacdc93353, linuxarm64: ami-01adb2cc0dd49e999, windows: ami-050cbc763f520f830 } - me-south-1 : { linuxamd64: ami-012fbb242739a1f1a, linuxarm64: ami-08d934ccc6cddc763, windows: ami-009277c4bc01371da } - sa-east-1 : { linuxamd64: ami-05d829f95ceb5f292, linuxarm64: ami-00390af183eab4b74, windows: ami-08e8e717a53b6b2b5 } + us-east-1 : { linuxamd64: ami-0d870d6249c932e3f, linuxarm64: ami-0a3d7a30823a79bed, windows: ami-0cc1cf707c9bde297 } + us-east-2 : { linuxamd64: ami-0f3019cc4ae209e8d, linuxarm64: ami-06fbf388ceadee136, windows: ami-0cf377d071681be17 } + us-west-1 : { linuxamd64: ami-0bc45e1a1e3b81024, linuxarm64: ami-03ccc79e335ddfeb2, windows: ami-0bf3b5f6168efcd16 } + us-west-2 : { linuxamd64: ami-0fb582405657e5e7d, linuxarm64: ami-019482f9dad0e6c6c, windows: ami-01a7cfec21679fdc6 } + af-south-1 : { linuxamd64: ami-0472a3974f5fc2b3e, linuxarm64: ami-031d70266097ac913, windows: ami-0c9d2380139ca74ae } + ap-east-1 : { linuxamd64: ami-0d01d071f6cb4531f, linuxarm64: ami-076b30b50dd891795, windows: ami-0047ed2d7146a7bfd } + ap-south-1 : { linuxamd64: ami-03dcda51307fc8cb5, linuxarm64: ami-012d6489d7405cac9, windows: ami-075d2d36dfbf32867 } + ap-northeast-2 : { linuxamd64: ami-0f2d7daa735810eee, linuxarm64: ami-0a2cc2b93142ea24a, windows: ami-08cb758a9ddc43059 } + ap-northeast-1 : { linuxamd64: ami-04051311bdfde36f3, linuxarm64: ami-09e4f9370ec79c3ba, windows: ami-05b6ec0208eb2a58a } + ap-southeast-2 : { linuxamd64: ami-0dca9e865ae37c7ed, linuxarm64: ami-05d80d286a7bade59, windows: ami-0667ba4d9ff4dc9d7 } + ap-southeast-1 : { linuxamd64: ami-041a2f49842dfedd1, linuxarm64: ami-04b58654a0075cf44, windows: ami-012d7bd61f9b1d6b7 } + ca-central-1 : { linuxamd64: ami-00e53b8bc82f9c9db, linuxarm64: ami-0f16c32fb617d5a48, windows: ami-088bf9470ff92506c } + eu-central-1 : { linuxamd64: ami-05c5209917612c4ef, linuxarm64: ami-08bb74ee0e90d2670, windows: ami-06826a0d3b4c7e1ab } + eu-west-1 : { linuxamd64: ami-06274dc3861664987, linuxarm64: ami-07ccdfbf8eaa3c951, windows: ami-036f1d5605b9dbf1e } + eu-west-2 : { linuxamd64: ami-086942d9992b4e6d3, linuxarm64: ami-0008aaf782bc53012, windows: ami-0825cacfdb3a8dcd6 } + eu-south-1 : { linuxamd64: ami-0e482f53f6f51d3e3, linuxarm64: ami-08c23003032d5ca62, windows: ami-0cb004f172e2b7007 } + eu-west-3 : { linuxamd64: ami-087631959c2b65a0b, linuxarm64: ami-08216f2c9c2778a91, windows: ami-03f5013af4a1e133b } + eu-north-1 : { linuxamd64: ami-0d769ff12cca6d68d, linuxarm64: ami-06ad99587f4894bbd, windows: ami-0406a7b8f45352245 } + me-south-1 : { linuxamd64: ami-0b82f151c4fed9e4a, linuxarm64: ami-00b941cafd5b87c70, windows: ami-0bf6572cc349f9447 } + sa-east-1 : { linuxamd64: ami-09db409e3b9399d3b, linuxarm64: ami-002802cb7c79d6fd8, windows: ami-0d9f87a270ecf8c21 } Resources: Vpc: @@ -891,6 +959,18 @@ Resources: Name: !Sub "/${AWS::StackName}/buildkite/agent-token" Type: String Value: !Ref BuildkiteAgentToken + + PipelineSigningKMSKey: + Type: AWS::KMS::Key + Condition: CreatePipelineSigningKMSKey + DeletionPolicy: Retain + Properties: + Description: Key used to sign and verify pipelines + KeySpec: !Ref PipelineSigningKMSKeySpec + KeyUsage: SIGN_VERIFY + Tags: + - Key: Name + Value: !Sub '${AWS::StackName}-PipelineSigningKey' # Allow ec2 instances to assume a role and be granted the IAMPolicies IAMInstanceProfile: @@ -923,6 +1003,26 @@ Resources: - !Ref 'AWS::NoValue' - !Ref 'AWS::NoValue' Policies: + - !If + - HasPipelineSigningKMSKey + - PolicyName: PipelineSigningKMSKeyAccess + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + !If + - HasSigningKMSAccessSignAndVerify + - - kms:Sign + - kms:Verify + - kms:GetPublicKey + - - kms:Verify + - kms:GetPublicKey + Resource: !If + - CreatePipelineSigningKMSKey + - !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/${PipelineSigningKMSKey} + - !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/${PipelineSigningKMSKeyId} + - !Ref 'AWS::NoValue' - !If - UseCustomerManagedKeyForParameterStore - PolicyName: DecryptAgentToken @@ -1237,7 +1337,7 @@ Resources: powershell -file C:\buildkite-agent\bin\bk-configure-docker.ps1 >> C:\buildkite-agent\elastic-stack.log $Env:BUILDKITE_STACK_NAME="${AWS::StackName}" - $Env:BUILDKITE_STACK_VERSION="v6.23.0" + $Env:BUILDKITE_STACK_VERSION="v6.27.0" $Env:BUILDKITE_SCALE_IN_IDLE_PERIOD="${ScaleInIdlePeriod}" $Env:BUILDKITE_SECRETS_BUCKET="${LocalSecretsBucket}" $Env:BUILDKITE_SECRETS_BUCKET_REGION="${LocalSecretsBucketRegion}" @@ -1251,6 +1351,8 @@ Resources: $Env:BUILDKITE_QUEUE="${BuildkiteQueue}" $Env:BUILDKITE_AGENT_ENABLE_GIT_MIRRORS="${BuildkiteAgentEnableGitMirrors}" $Env:BUILDKITE_ELASTIC_BOOTSTRAP_SCRIPT="${BootstrapScriptUrl}" + $Env:BUILDKITE_AGENT_SIGNING_KMS_KEY="${PipelineSigningKMSKey}" + $Env:BUILDKITE_AGENT_SIGNING_FAILURE_BEHAVIOR="${PipelineSigningVerificationFailureBehavior}" $Env:BUILDKITE_ENV_FILE_URL="${AgentEnvFileUrl}" $Env:BUILDKITE_AUTHORIZED_USERS_URL="${AuthorizedUsersUrl}" $Env:BUILDKITE_ECR_POLICY="${ECRAccessPolicy}" @@ -1268,6 +1370,7 @@ Resources: LocalSecretsBucket: !If [ CreateSecretsBucket, !Ref ManagedSecretsBucket, !Ref SecretsBucket ], LocalSecretsBucketRegion: !If [ CreateSecretsBucket, !Ref "AWS::Region", !Ref SecretsBucketRegion ], AgentTokenPath: !If [ UseCustomerManagedParameterPath, !Ref BuildkiteAgentTokenParameterStorePath, !Ref BuildkiteAgentTokenParameter ], + PipelineSigningKMSKey: !If [ CreatePipelineSigningKMSKey, !Ref PipelineSigningKMSKey, !Ref PipelineSigningKMSKeyId ], } - !Sub - | @@ -1296,7 +1399,7 @@ Resources: Content-Type: text/x-shellscript; charset="us-ascii" #!/bin/bash -v BUILDKITE_STACK_NAME="${AWS::StackName}" \ - BUILDKITE_STACK_VERSION="v6.23.0" \ + BUILDKITE_STACK_VERSION="v6.27.0" \ BUILDKITE_SCALE_IN_IDLE_PERIOD="${ScaleInIdlePeriod}" \ BUILDKITE_SECRETS_BUCKET="${LocalSecretsBucket}" \ BUILDKITE_SECRETS_BUCKET_REGION="${LocalSecretsBucketRegion}" \ @@ -1308,6 +1411,8 @@ Resources: BUILDKITE_AGENT_TRACING_BACKEND="${BuildkiteAgentTracingBackend}" \ BUILDKITE_AGENT_RELEASE="${BuildkiteAgentRelease}" \ BUILDKITE_AGENT_CANCEL_GRACE_PERIOD="${BuildkiteAgentCancelGracePeriod}" \ + BUILDKITE_AGENT_SIGNING_KMS_KEY="${PipelineSigningKMSKey}" \ + BUILDKITE_AGENT_SIGNING_FAILURE_BEHAVIOR="${PipelineSigningVerificationFailureBehavior}" \ BUILDKITE_QUEUE="${BuildkiteQueue}" \ BUILDKITE_AGENT_ENABLE_GIT_MIRRORS="${BuildkiteAgentEnableGitMirrors}" \ BUILDKITE_ELASTIC_BOOTSTRAP_SCRIPT="${BootstrapScriptUrl}" \ @@ -1330,6 +1435,7 @@ Resources: LocalSecretsBucket: !If [ CreateSecretsBucket, !Ref ManagedSecretsBucket, !Ref SecretsBucket ], LocalSecretsBucketRegion: !If [ CreateSecretsBucket, !Ref "AWS::Region", !Ref SecretsBucketRegion ], AgentTokenPath: !If [ UseCustomerManagedParameterPath, !Ref BuildkiteAgentTokenParameterStorePath, !Ref BuildkiteAgentTokenParameter ], + PipelineSigningKMSKey: !If [ CreatePipelineSigningKMSKey, !Ref PipelineSigningKMSKey, !Ref PipelineSigningKMSKeyId ], } AgentAutoScaleGroup: From d0510217e98336ede00fb2f67fcbdb90d6cf7ea0 Mon Sep 17 00:00:00 2001 From: L Suzuki <10970711+l-suzuki@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:03:57 +1200 Subject: [PATCH 115/369] Update docs to reflect new naming for Rules params, and to specify that slugs or uuids are accepted (#2984) --- pages/apis/graphql/cookbooks/rules.md | 2 +- pages/apis/rest_api/rules.md | 8 ++++---- pages/pipelines/rules.md | 16 ++++++++-------- pages/pipelines/rules/manage.md | 22 +++++++++++++--------- vendor/emojis | 2 +- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/pages/apis/graphql/cookbooks/rules.md b/pages/apis/graphql/cookbooks/rules.md index 84c30ac043..939617ec42 100644 --- a/pages/apis/graphql/cookbooks/rules.md +++ b/pages/apis/graphql/cookbooks/rules.md @@ -50,7 +50,7 @@ mutation { ruleCreate(input: { organizationId: "organization-id", type: "pipeline.trigger_build.pipeline", - value: "{\"source_pipeline_uuid\":\"{uuid-of-source-pipeline}\",\"target_pipeline_uuid\":\"{uuid-of-target-pipeline}\"}" + value: "{\"source_pipeline\":\"{uuid-of-source-pipeline}\",\"target_pipeline\":\"{uuid-of-target-pipeline}\"}" }) { rule { id diff --git a/pages/apis/rest_api/rules.md b/pages/apis/rest_api/rules.md index 1395469ff0..05b232b544 100644 --- a/pages/apis/rest_api/rules.md +++ b/pages/apis/rest_api/rules.md @@ -93,8 +93,8 @@ curl -H "Authorization: Bearer $TOKEN" \ -d '{ "type": "pipeline.trigger_build.pipeline", "value": { - "source_pipeline_uuid": "16f3b56f-4934-4546-923c-287859851332", - "target_pipeline_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac" + "source_pipeline": "16f3b56f-4934-4546-923c-287859851332", + "target_pipeline": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac" } }' ``` @@ -139,8 +139,8 @@ Required [request body properties](/docs/api#request-body-properties): value - A JSON object containing the value fields for the rule.
- Example: {"source_pipeline_uuid": "16f3b56f-4934-4546-923c-287859851332", "target_pipeline_uuid": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac"} + A JSON object containing the value fields for the rule. source_pipeline and target_pipeline fields accept either a pipeline UUID or a pipeline slug.
+ Example: {"source_pipeline": "16f3b56f-4934-4546-923c-287859851332", "target_pipeline": "d07d5d84-d1bd-479c-902c-ce8a01ce5aac"} diff --git a/pages/pipelines/rules.md b/pages/pipelines/rules.md index 74a56d247d..708719e3f7 100644 --- a/pages/pipelines/rules.md +++ b/pages/pipelines/rules.md @@ -29,16 +29,16 @@ This rule type allows one pipeline to trigger another, where: { "rule": "pipeline.trigger_build.pipeline", "value": { - "source_pipeline_uuid": "pipeline-uuid", - "target_pipeline_uuid": "pipeline-uuid" + "source_pipeline": "pipeline-uuid-or-slug", + "target_pipeline": "pipeline-uuid-or-slug" } } ``` where: -- `source_pipeline_uuid` is the UUID of the pipeline that's allowed to trigger another pipeline. -- `target_pipeline_uuid` is the UUID of the pipeline that can be triggered by the `source_pipeline_uuid` pipeline. +- `source_pipeline` is the UUID or slug of the pipeline that's allowed to trigger another pipeline. +- `target_pipeline` is the UUID or slug of the pipeline that can be triggered by the `source_pipeline` pipeline. Learn more about creating rules in [Manage rules](/docs/pipelines/rules/manage). @@ -61,16 +61,16 @@ This rule type allows one pipeline to access (that is, with read-only permission { "rule": "pipeline.artifacts_read.pipeline", "value": { - "source_pipeline_uuid": "pipeline-uuid", - "target_pipeline_uuid": "pipeline-uuid" + "source_pipeline": "pipeline-uuid-or-slug", + "target_pipeline": "pipeline-uuid-or-slug" } } ``` where: -- `source_pipeline_uuid` is the UUID of the pipeline that's allowed to access the artifacts from another pipeline. -- `target_pipeline_uuid` is the UUID of the pipeline whose artifacts can be accessed by jobs in the `source_pipeline_uuid` pipeline. +- `source_pipeline` is the UUID or slug of the pipeline that's allowed to access the artifacts from another pipeline. +- `target_pipeline` is the UUID or slug of the pipeline whose artifacts can be accessed by jobs in the `source_pipeline` pipeline. Learn more about creating rules in [Manage rules](/docs/pipelines/rules/manage). diff --git a/pages/pipelines/rules/manage.md b/pages/pipelines/rules/manage.md index 094b8c5bdd..a15c7ce1e0 100644 --- a/pages/pipelines/rules/manage.md +++ b/pages/pipelines/rules/manage.md @@ -16,7 +16,7 @@ To create a new rule using the Buildkite interface: 1. Under **Rule Type**, select the [type of rule](/docs/pipelines/rules#rule-types) to be created, that is, either **pipeline.trigger_build.pipeline** or **pipeline.artifacts_read.pipeline**. -1. Under **Rule Document**, specify the relevant `pipeline-uuid` (UUID) values for both the `source_pipeline_uuid` and `target_pipeline_uuid` pipelines, of your [**pipeline.trigger_build.pipeline**](/docs/pipelines/rules#rule-types-pipeline-dot-trigger-build-dot-pipeline) or [**pipeline.artifacts_read.pipeline**](/docs/pipelines/rules#rule-types-pipeline-dot-artifacts-read-dot-pipeline) rule. You can find the UUID values for these pipelines on the pipelines' respective **Settings** page under the **GraphQL API integration** section. +1. Under **Rule Document**, specify the relevant values (either a pipeline UUID or a pipeline slug) for both the `source_pipeline` and `target_pipeline` pipelines, of your [**pipeline.trigger_build.pipeline**](/docs/pipelines/rules#rule-types-pipeline-dot-trigger-build-dot-pipeline) or [**pipeline.artifacts_read.pipeline**](/docs/pipelines/rules#rule-types-pipeline-dot-artifacts-read-dot-pipeline) rule. You can find the UUID values for these pipelines on the pipelines' respective **Settings** page under the **GraphQL API integration** section. 1. Select **Submit**. @@ -33,8 +33,8 @@ curl -H "Authorization: Bearer $TOKEN" \ -d '{ "rule": "pipeline.trigger_build.pipeline", "value": { - "source_pipeline_uuid": "{pipeline.uuid}", - "target_pipeline_uuid": "{pipeline.uuid}" + "source_pipeline": "{pipeline-uuid-or-slug}", + "target_pipeline": "{pipeline-uuid-or-slug}" } }' ``` @@ -47,11 +47,13 @@ where: - `rule` is the [type of rule](/docs/pipelines/rules#rule-types) to be created, that is, either `pipeline.trigger_build.pipeline` or `pipeline.artifacts_read.pipeline`. -- `{pipeline.uuid}` value for `source_pipeline_uuid` and `target_pipeline_uuid` can be obtained: +- `source_pipeline` and `target_pipeline` accept either a pipeline slug or UUID. + +- Pipeline UUID values for `source_pipeline` and `target_pipeline` can be obtained: * From the **Pipeline Settings** page of the appropriate pipeline. To do this: 1. Select **Pipelines** (in the global navigation) > the specific pipeline > **Settings**. - 1. Once on the **Pipeline Settings** page, copy the `UUID` value from the **GraphQL API Integration** section, which is the `{pipeline.uuid}` value. + 1. Once on the **Pipeline Settings** page, copy the `UUID` value from the **GraphQL API Integration** section * By running the [List pipelines](/docs/apis/rest-api/pipelines#list-pipelines) REST API query to obtain this value from `id` in the response from the specific pipeline. For example: @@ -69,7 +71,7 @@ mutation { ruleCreate(input: { organizationId: "organization-id", type: "pipeline.trigger_build.pipeline", - value: "{\"source_pipeline_uuid\":\"pipeline-uuid\",\"target_pipeline_uuid\":\"pipeline-uuid\"}" + value: "{\"source_pipeline\":\"pipeline-uuid-or-slug\",\"target_pipeline\":\"pipeline-uuid-or-slug\"}" }) { rule { id @@ -103,11 +105,13 @@ where: - `type` is the [type of rule](/docs/pipelines/rules#rule-types) to be created, that is, either `pipeline.trigger_build.pipeline` or `pipeline.artifacts_read.pipeline`. -- `pipeline-uuid` value for `source_pipeline_uuid` and `target_pipeline_uuid` can be obtained: +- `source_pipeline` and `target_pipeline` accept either a pipeline slug or UUID. + +- Pipeline UUID values for `source_pipeline` and `target_pipeline` can be obtained: * From the **Pipeline Settings** page of the appropriate pipeline. To do this: 1. Select **Pipelines** (in the global navigation) > the specific pipeline > **Settings**. - 1. Once on the **Pipeline Settings** page, copy the `UUID` value from the **GraphQL API Integration** section, which is the `pipeline-uuid` value. + 1. Once on the **Pipeline Settings** page, copy the `UUID` value from the **GraphQL API Integration** section * By running the `getCurrentUsersOrgs` GraphQL API query to obtain the organization slugs for the current user's accessible organizations, then [getOrgPipelines](/docs/apis/graphql/schemas/query/organization) query to obtain the pipeline's `uuid` in the response. For example: @@ -140,7 +144,7 @@ where: uuid name } - } + } } } } diff --git a/vendor/emojis b/vendor/emojis index 223909f2e0..f113dde655 160000 --- a/vendor/emojis +++ b/vendor/emojis @@ -1 +1 @@ -Subproject commit 223909f2e0449df84b73471732653de6173cc11e +Subproject commit f113dde655aaba3d45515686215485cfeb3cb182 From 837c3f83846dce34422f144a30d44f41425bb4cb Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Fri, 13 Sep 2024 14:10:08 +1000 Subject: [PATCH 116/369] add public sync pipeline.yml --- .buildkite/pipeline.sync.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .buildkite/pipeline.sync.yml diff --git a/.buildkite/pipeline.sync.yml b/.buildkite/pipeline.sync.yml new file mode 100644 index 0000000000..b4b87cbea6 --- /dev/null +++ b/.buildkite/pipeline.sync.yml @@ -0,0 +1,9 @@ +env: + PUBLIC_REPO: git@github.com:buildkite/docs.git +steps: + - label: Sync to public mirror + branches: main + commands: | + echo "+++ :git: Syncing to public mirror" + + git push --force "${PUBLIC_REPO}" ${BUILDKITE_COMMIT}:main From b4606be5daa4d197169fbd9990d895c90e24ed9f Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Fri, 13 Sep 2024 14:32:01 +1000 Subject: [PATCH 117/369] remove --force from sync to verify if it will fast-forward --- .buildkite/pipeline.sync.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/pipeline.sync.yml b/.buildkite/pipeline.sync.yml index b4b87cbea6..44196a91cb 100644 --- a/.buildkite/pipeline.sync.yml +++ b/.buildkite/pipeline.sync.yml @@ -6,4 +6,4 @@ steps: commands: | echo "+++ :git: Syncing to public mirror" - git push --force "${PUBLIC_REPO}" ${BUILDKITE_COMMIT}:main + git push "${PUBLIC_REPO}" ${BUILDKITE_COMMIT}:main From 7ba226fcc34fb0b7c589fea8d0c10d28ed969c21 Mon Sep 17 00:00:00 2001 From: James Healy Date: Fri, 13 Sep 2024 14:42:55 +1000 Subject: [PATCH 118/369] Upgrade to ruby 3.3.5 I'm not aware of any bug fixes we need, just bumping for good hygiene. --- .ruby-version | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ruby-version b/.ruby-version index 619b537668..fa7adc7ac7 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.3.3 +3.3.5 diff --git a/Dockerfile b/Dockerfile index bbcd1d2b0f..aa945f6e3f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=public.ecr.aws/docker/library/ruby:3.3.3-slim-bookworm@sha256:bc6372a998e79b5154c8132d1b3e0287dc656249f71f48487a1ecf0d46c9c080 +ARG BASE_IMAGE=public.ecr.aws/docker/library/ruby:3.3.5-slim-bookworm ARG NODE_IMAGE=public.ecr.aws/docker/library/node:18-bookworm-slim@sha256:d2d8a6420c9fc6b7b403732d3a3c5395d016ebc4996f057aad1aded74202a476 FROM $BASE_IMAGE AS builder From c18f0350ef0236b8ad33a2b91a73f7c66e0949d2 Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Fri, 13 Sep 2024 15:21:04 +1000 Subject: [PATCH 119/369] update pipeline slug --- .buildkite/pipeline.deploy.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.buildkite/pipeline.deploy.yml b/.buildkite/pipeline.deploy.yml index 44ff7d9637..04fd86238a 100644 --- a/.buildkite/pipeline.deploy.yml +++ b/.buildkite/pipeline.deploy.yml @@ -9,7 +9,7 @@ steps: queue: elastic-runners plugins: - aws-assume-role-with-web-identity#v1.0.0: - role-arn: arn:aws:iam::${ECR_ACCOUNT_ID}:role/pipeline-buildkite-docs-main + role-arn: arn:aws:iam::${ECR_ACCOUNT_ID}:role/pipeline-buildkite-docs-private - ecr#v2.7.0: login: true account-ids: ${ECR_ACCOUNT_ID} @@ -21,7 +21,7 @@ steps: depends_on: "ecr-push" plugins: - aws-assume-role-with-web-identity#v1.0.0: - role-arn: arn:aws:iam::${ECR_ACCOUNT_ID}:role/pipeline-buildkite-docs-main + role-arn: arn:aws:iam::${ECR_ACCOUNT_ID}:role/pipeline-buildkite-docs-private - buildkite/ecr-scan-results#v2.0.0: image-name: "${ECR_REPO}:${BUILDKITE_BUILD_NUMBER}" fail-build-on-plugin-failure: true @@ -52,7 +52,7 @@ steps: command: scripts/deploy-ecs plugins: - aws-assume-role-with-web-identity#v1.0.0: - role-arn: arn:aws:iam::${ECR_ACCOUNT_ID}:role/pipeline-buildkite-docs-main + role-arn: arn:aws:iam::${ECR_ACCOUNT_ID}:role/pipeline-buildkite-docs-private - wait From 7c7ae433d7cc886cacb4101a1e785bac66553ce3 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 16 Sep 2024 10:35:26 +1000 Subject: [PATCH 120/369] More refactoring of 'Test Analytics' links and refs to 'Test Engine' rebranding. --- .github/labeler.yml | 4 +- Gemfile.lock | 2 +- app/views/homepage/_products.html.erb | 6 +-- app/views/homepage/_references.html.erb | 26 ++++++------- data/nav.yml | 22 ++++++----- data/tiles.yml | 38 +++++++++---------- .../{test_analytics.svg => test_engine.svg} | 0 .../_flaky_tests_query_strings.md | 0 .../_runs_list_query_strings.md | 0 .../{analytics => test_engine}/flaky_tests.md | 2 +- .../{analytics => test_engine}/runs.md | 2 +- .../{analytics => test_engine}/suites.md | 0 .../{analytics => test_engine}/tests.md | 0 13 files changed, 52 insertions(+), 50 deletions(-) rename images/docs/home/{test_analytics.svg => test_engine.svg} (100%) rename pages/apis/rest_api/{analytics => test_engine}/_flaky_tests_query_strings.md (100%) rename pages/apis/rest_api/{analytics => test_engine}/_runs_list_query_strings.md (100%) rename pages/apis/rest_api/{analytics => test_engine}/flaky_tests.md (92%) rename pages/apis/rest_api/{analytics => test_engine}/runs.md (96%) rename pages/apis/rest_api/{analytics => test_engine}/suites.md (100%) rename pages/apis/rest_api/{analytics => test_engine}/tests.md (100%) diff --git a/.github/labeler.yml b/.github/labeler.yml index 3d70159e2e..0e34d6298f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -2,8 +2,8 @@ agent: - pages/agent/**/* cli: - pages/cli/**/* -test-analytics: - - pages/test_analytics/**/* +test-engine: + - pages/test_engine/**/* pipelines: - pages/pipelines/**/* - pages/tutorials/**/* diff --git a/Gemfile.lock b/Gemfile.lock index 6b50c7268c..866c41b59f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -190,7 +190,7 @@ DEPENDENCIES web-console RUBY VERSION - ruby 3.3.3p89 + ruby 3.3.5p100 BUNDLED WITH 2.4.7 diff --git a/app/views/homepage/_products.html.erb b/app/views/homepage/_products.html.erb index a4ccac6a10..45f5a7854b 100644 --- a/app/views/homepage/_products.html.erb +++ b/app/views/homepage/_products.html.erb @@ -9,11 +9,11 @@

Powerful CI/CD built to scale on your infrastructure

- +
- Test Analytics graphic + Test Engine graphic
-

Test Analytics

+

Test Engine

Real-time tracking and monitoring for your tests

diff --git a/app/views/homepage/_references.html.erb b/app/views/homepage/_references.html.erb index b40ef3be60..20a0a1406f 100644 --- a/app/views/homepage/_references.html.erb +++ b/app/views/homepage/_references.html.erb @@ -43,6 +43,19 @@ } ] }, + { + title: "Test Engine", + links: [ + { + title: "JSON test results", + link: "/docs/test-engine/importing-json" + }, + { + title: "Environment variables", + link: "/docs/test-engine/ci-environments" + } + ] + }, { title: "API", links: [ @@ -59,19 +72,6 @@ link: "/docs/apis/webhooks" } ] - }, - { - title: "Test Analytics", - links: [ - { - title: "JSON test results", - link: "/docs/test-analytics/importing-json" - }, - { - title: "Environment variables", - link: "/docs/test-analytics/ci-environments" - } - ] } ] %> diff --git a/data/nav.yml b/data/nav.yml index 8e3d28078b..c2c87647fc 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -540,16 +540,6 @@ path: "apis/agent-api" - name: "Metrics" path: "apis/agent-api/metrics" - - name: "Analytics" - children: - - name: "Flaky tests" - path: "apis/rest-api/analytics/flaky-tests" - - name: "Runs" - path: "apis/rest-api/analytics/runs" - - name: "Suites" - path: "apis/rest-api/analytics/suites" - - name: "Tests" - path: "apis/rest-api/analytics/tests" - name: "Organizations" children: - name: "Overview" @@ -608,6 +598,18 @@ path: "apis/rest-api/teams/pipelines" - name: "Suites" path: "apis/rest-api/teams/suites" + - name: "Test Engine " + # Keep space at end to prevent "Test Engine" in global nav bar being + # highlighted when any child page of "API > REST > Pipelines" is selected. + children: + - name: "Flaky tests" + path: "apis/rest-api/test-engine/flaky-tests" + - name: "Runs" + path: "apis/rest-api/test-engine/runs" + - name: "Suites" + path: "apis/rest-api/test-engine/suites" + - name: "Tests" + path: "apis/rest-api/test-engine/tests" - name: "GraphQL" children: - name: Overview diff --git a/data/tiles.yml b/data/tiles.yml index f120f99eb3..0d82a648f9 100644 --- a/data/tiles.yml +++ b/data/tiles.yml @@ -1,46 +1,46 @@ --- -test_analytics_features: +test_engine_features: - title: "Deep performance analysis" - url: "/docs/test-analytics/test-suites#trends-and-analysis" + url: "/docs/test-engine/test-suites#trends-and-analysis" desc: "Automatic tracing across your test suite, deeply integrated with your programming language and test framework." - title: "Find and fix flaky tests" - url: "/docs/test-analytics/test-suites#detecting-flaky-tests" + url: "/docs/test-engine/test-suites#detecting-flaky-tests" desc: "Quickly identify which tests are the most disruptive for your team, and get a head-start on fixing them." -test_analytics_guides: +test_engine_guides: - title: "Languages" links: - text: "Ruby" - url: "/docs/test-analytics/ruby-collectors" + url: "/docs/test-engine/ruby-collectors" - text: "JavaScript" - url: "/docs/test-analytics/javascript-collectors" + url: "/docs/test-engine/javascript-collectors" - text: "Swift" - url: "/docs/test-analytics/swift-collectors" + url: "/docs/test-engine/swift-collectors" - text: "Android" - url: "/docs/test-analytics/android-collectors" + url: "/docs/test-engine/android-collectors" - text: "Python" - url: "/docs/test-analytics/python-collectors" + url: "/docs/test-engine/python-collectors" - text: "Go" - url: "/docs/test-analytics/golang-collectors" + url: "/docs/test-engine/golang-collectors" - text: ".NET" - url: "/docs/test-analytics/dotnet-collectors" + url: "/docs/test-engine/dotnet-collectors" - text: "Elixir" - url: "/docs/test-analytics/elixir-collectors" + url: "/docs/test-engine/elixir-collectors" - text: "Rust" - url: "/docs/test-analytics/rust-collectors" + url: "/docs/test-engine/rust-collectors" - text: "Java" - url: "/docs/test-analytics/java" + url: "/docs/test-engine/java" - text: "Other languages" - url: "/docs/test-analytics/other-collectors" + url: "/docs/test-engine/other-collectors" - title: "References" links: - text: "Uploading JSON data" - url: "/docs/test-analytics/importing-json" + url: "/docs/test-engine/importing-json" - text: "Uploading JUnit XML results" - url: "/docs/test-analytics/importing-junit-xml" + url: "/docs/test-engine/importing-junit-xml" - text: "Build your own collector" - url: "/docs/test-analytics/your-own-collectors" + url: "/docs/test-engine/your-own-collectors" - text: "CI environment variables" - url: "/docs/test-analytics/ci-environments" + url: "/docs/test-engine/ci-environments" - "" integrations: - title: "Plugins" diff --git a/images/docs/home/test_analytics.svg b/images/docs/home/test_engine.svg similarity index 100% rename from images/docs/home/test_analytics.svg rename to images/docs/home/test_engine.svg diff --git a/pages/apis/rest_api/analytics/_flaky_tests_query_strings.md b/pages/apis/rest_api/test_engine/_flaky_tests_query_strings.md similarity index 100% rename from pages/apis/rest_api/analytics/_flaky_tests_query_strings.md rename to pages/apis/rest_api/test_engine/_flaky_tests_query_strings.md diff --git a/pages/apis/rest_api/analytics/_runs_list_query_strings.md b/pages/apis/rest_api/test_engine/_runs_list_query_strings.md similarity index 100% rename from pages/apis/rest_api/analytics/_runs_list_query_strings.md rename to pages/apis/rest_api/test_engine/_runs_list_query_strings.md diff --git a/pages/apis/rest_api/analytics/flaky_tests.md b/pages/apis/rest_api/test_engine/flaky_tests.md similarity index 92% rename from pages/apis/rest_api/analytics/flaky_tests.md rename to pages/apis/rest_api/test_engine/flaky_tests.md index f5de36c0b7..d621af1cbe 100644 --- a/pages/apis/rest_api/analytics/flaky_tests.md +++ b/pages/apis/rest_api/test_engine/flaky_tests.md @@ -30,7 +30,7 @@ curl -H "Authorization: Bearer $TOKEN" \ Optional [query string parameters](/docs/api#query-string-parameters): -<%= render_markdown partial: 'apis/rest_api/analytics/flaky_tests_query_strings' %> +<%= render_markdown partial: 'apis/rest_api/test_engine/flaky_tests_query_strings' %> Required scope: `read_suites` diff --git a/pages/apis/rest_api/analytics/runs.md b/pages/apis/rest_api/test_engine/runs.md similarity index 96% rename from pages/apis/rest_api/analytics/runs.md rename to pages/apis/rest_api/test_engine/runs.md index f8c52d183b..bfe279c109 100644 --- a/pages/apis/rest_api/analytics/runs.md +++ b/pages/apis/rest_api/test_engine/runs.md @@ -26,7 +26,7 @@ curl -H "Authorization: Bearer $TOKEN" \ Optional [query string parameters](/docs/api#query-string-parameters): -<%= render_markdown partial: 'apis/rest_api/analytics/runs_list_query_strings' %> +<%= render_markdown partial: 'apis/rest_api/test_engine/runs_list_query_strings' %> Required scope: `read_suites` diff --git a/pages/apis/rest_api/analytics/suites.md b/pages/apis/rest_api/test_engine/suites.md similarity index 100% rename from pages/apis/rest_api/analytics/suites.md rename to pages/apis/rest_api/test_engine/suites.md diff --git a/pages/apis/rest_api/analytics/tests.md b/pages/apis/rest_api/test_engine/tests.md similarity index 100% rename from pages/apis/rest_api/analytics/tests.md rename to pages/apis/rest_api/test_engine/tests.md From 8bfb9fc251db89b84263b2e269ba8ef212af0879 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 16 Sep 2024 10:48:42 +1000 Subject: [PATCH 121/369] Fix RSpec build failure. --- spec/features/emojicom_rendering_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/emojicom_rendering_spec.rb b/spec/features/emojicom_rendering_spec.rb index c5a1d69ca9..4148ec516d 100644 --- a/spec/features/emojicom_rendering_spec.rb +++ b/spec/features/emojicom_rendering_spec.rb @@ -11,7 +11,7 @@ context "landing page" do scenario "does display emojicom widget" do - visit "/docs/test-analytics" + visit "/docs/test-engine" expect(page).to have_css "#emojicom-widget-inline" end From affff811226f1a93252d3f128184e49fce49297f Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 16 Sep 2024 11:40:24 +1000 Subject: [PATCH 122/369] Rebrand 'Test Analytics' product name in docs to 'Test Engine'. --- ...ml => test_engine_json_fields.schema.yaml} | 0 ...ml => test_engine_json_fields_detail.yaml} | 0 ..._engine_json_fields_failure_expanded.yaml} | 0 ...l => test_engine_json_fields_history.yaml} | 0 ...yaml => test_engine_json_fields_span.yaml} | 0 ... test_engine_json_fields_test_result.yaml} | 2 +- ...a.yaml => test_engine_run_env.schema.yaml} | 0 ..._run_env.yaml => test_engine_run_env.yaml} | 0 data/content/test_splitting_env.yaml | 2 +- pages/test_engine/android_collectors.md | 12 ++++---- pages/test_engine/ci_environments.md | 6 ++-- pages/test_engine/dotnet_collectors.md | 4 +-- pages/test_engine/elixir_collectors.md | 6 ++-- pages/test_engine/golang_collectors.md | 4 +-- pages/test_engine/importing_json.md | 23 ++++++++------- pages/test_engine/importing_junit_xml.md | 14 +++++----- pages/test_engine/javascript_collectors.md | 24 ++++++++-------- pages/test_engine/other_collectors.md | 2 +- pages/test_engine/permissions.md | 4 +-- pages/test_engine/python_collectors.md | 12 ++++---- pages/test_engine/ruby_collectors.md | 28 +++++++++---------- pages/test_engine/rust_collectors.md | 4 +-- pages/test_engine/swift_collectors.md | 12 ++++---- pages/test_engine/test_ownership.md | 26 ++++++++--------- pages/test_engine/test_splitting.md | 2 +- pages/test_engine/test_suites.md | 20 ++++++------- pages/test_engine/your_own_collectors.md | 4 +-- 27 files changed, 105 insertions(+), 106 deletions(-) rename data/content/{test_analytics_json_fields.schema.yaml => test_engine_json_fields.schema.yaml} (100%) rename data/content/{test_analytics_json_fields_detail.yaml => test_engine_json_fields_detail.yaml} (100%) rename data/content/{test_analytics_json_fields_failure_expanded.yaml => test_engine_json_fields_failure_expanded.yaml} (100%) rename data/content/{test_analytics_json_fields_history.yaml => test_engine_json_fields_history.yaml} (100%) rename data/content/{test_analytics_json_fields_span.yaml => test_engine_json_fields_span.yaml} (100%) rename data/content/{test_analytics_json_fields_test_result.yaml => test_engine_json_fields_test_result.yaml} (94%) rename data/content/{test_analytics_run_env.schema.yaml => test_engine_run_env.schema.yaml} (100%) rename data/content/{test_analytics_run_env.yaml => test_engine_run_env.yaml} (100%) diff --git a/data/content/test_analytics_json_fields.schema.yaml b/data/content/test_engine_json_fields.schema.yaml similarity index 100% rename from data/content/test_analytics_json_fields.schema.yaml rename to data/content/test_engine_json_fields.schema.yaml diff --git a/data/content/test_analytics_json_fields_detail.yaml b/data/content/test_engine_json_fields_detail.yaml similarity index 100% rename from data/content/test_analytics_json_fields_detail.yaml rename to data/content/test_engine_json_fields_detail.yaml diff --git a/data/content/test_analytics_json_fields_failure_expanded.yaml b/data/content/test_engine_json_fields_failure_expanded.yaml similarity index 100% rename from data/content/test_analytics_json_fields_failure_expanded.yaml rename to data/content/test_engine_json_fields_failure_expanded.yaml diff --git a/data/content/test_analytics_json_fields_history.yaml b/data/content/test_engine_json_fields_history.yaml similarity index 100% rename from data/content/test_analytics_json_fields_history.yaml rename to data/content/test_engine_json_fields_history.yaml diff --git a/data/content/test_analytics_json_fields_span.yaml b/data/content/test_engine_json_fields_span.yaml similarity index 100% rename from data/content/test_analytics_json_fields_span.yaml rename to data/content/test_engine_json_fields_span.yaml diff --git a/data/content/test_analytics_json_fields_test_result.yaml b/data/content/test_engine_json_fields_test_result.yaml similarity index 94% rename from data/content/test_analytics_json_fields_test_result.yaml rename to data/content/test_engine_json_fields_test_result.yaml index b106c9ee0c..ad0a766857 100644 --- a/data/content/test_analytics_json_fields_test_result.yaml +++ b/data/content/test_engine_json_fields_test_result.yaml @@ -4,7 +4,7 @@ fields: type: UUIDv4 string enumerated_values: desc: | - A unique identifier for this test result. If a test execution with this UUID already exists in the Test Analytics database, this result is ignored. + A unique identifier for this test result. If a test execution with this UUID already exists in the Test Engine database, this result is ignored. examples: - 1b70486f-ca5f-4e6d-beb8-6347b2e49278 - name: scope diff --git a/data/content/test_analytics_run_env.schema.yaml b/data/content/test_engine_run_env.schema.yaml similarity index 100% rename from data/content/test_analytics_run_env.schema.yaml rename to data/content/test_engine_run_env.schema.yaml diff --git a/data/content/test_analytics_run_env.yaml b/data/content/test_engine_run_env.yaml similarity index 100% rename from data/content/test_analytics_run_env.yaml rename to data/content/test_engine_run_env.yaml diff --git a/data/content/test_splitting_env.yaml b/data/content/test_splitting_env.yaml index 48cd7ed914..422ae9aa71 100644 --- a/data/content/test_splitting_env.yaml +++ b/data/content/test_splitting_env.yaml @@ -26,7 +26,7 @@ mandatory: - Buildkite API access token with `read_suites`, `read_test_plan`, and `write_test_plan` scopes. You can create an [API access token](https://buildkite.com/user/api-access-tokens) from **Personal Settings** > **API Access Tokens** in the Buildkite interface. - name: BUILDKITE_SPLITTER_SUITE_SLUG desc: - - The slug of your Buildkite Test Analytics test suite. You can find the suite slug in the url for your test suite. + - The slug of your Buildkite Test Engine test suite. You can find the suite slug in the url for your test suite. - "For example, the slug for the url: `https://buildkite.com/organizations/my-organization/analytics/suites/my-suite` is `my-suite`." optional: diff --git a/pages/test_engine/android_collectors.md b/pages/test_engine/android_collectors.md index 4b3618a05a..1f6b1e0a97 100644 --- a/pages/test_engine/android_collectors.md +++ b/pages/test_engine/android_collectors.md @@ -4,7 +4,7 @@ toc: false # Android collectors -To use Test Analytics with your Android projects use the :github: [`test-collector-android`](https://github.com/buildkite/test-collector-android) package. +To use Test Engine with your Android projects use the :github: [`test-collector-android`](https://github.com/buildkite/test-collector-android) package. You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). @@ -89,14 +89,14 @@ Before you start, make sure your tests run with access to [CI environment variab 1. Commit and push your changes: ```bash - git checkout -b add-buildkite-test-analytics - git commit -am "Add Buildkite Test Analytics" - git push origin add-buildkite-test-analytics + git checkout -b add-buildkite-test-engine + git commit -am "Add Buildkite Test Engine" + git push origin add-buildkite-test-engine ``` -Once you're done, in your Test Analytics dashboard, you'll see analytics of test executions on all branches that include this code. +Once you're done, in your Test Engine dashboard, you'll see analytics of test executions on all branches that include this code. -If you don't see branch names, build numbers, or commit hashes in Test Analytics, then read [CI Environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. +If you don't see branch names, build numbers, or commit hashes in Test Engine, then read [CI Environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. ### Debugging diff --git a/pages/test_engine/ci_environments.md b/pages/test_engine/ci_environments.md index 26cd4b63f4..83ef11afa3 100644 --- a/pages/test_engine/ci_environments.md +++ b/pages/test_engine/ci_environments.md @@ -1,6 +1,6 @@ # CI environments -Buildkite Test Analytics collectors automatically detect common continuous integration (CI) environments. +Buildkite Test Engine collectors automatically detect common continuous integration (CI) environments. If available, test collectors gather information about your test runs, such as branch names and build IDs. Test collectors gather information from the following CI environments: @@ -103,7 +103,7 @@ run_env[key]=$GITHUB_ACTION-$GITHUB_RUN_NUMBER-$GITHUB_RUN_ATTEMPT ## Other CI providers If you're using other CI providers (or [containers](#containers-and-test-collectors)), then set environment variables for test collectors to gather information about your builds and tests. -If you don't set these environment variables, then Test Analytics lacks the details needed to produce useful reports. +If you don't set these environment variables, then Test Engine lacks the details needed to produce useful reports. Each environment variable corresponds to a `run_env` key in the payload `https://analytics-api.buildkite.com/v1/uploads`. Read [Importing JSON](/docs/test-engine/importing-json) to learn how these keys are used to make API calls. @@ -116,7 +116,7 @@ Each environment variable corresponds to a `run_env` key in the payload `https:/ - <% TEST_ANALYTICS_RUN_ENV['keys'].each do |key| -%> + <% TEST_ENGINE_RUN_ENV['keys'].each do |key| -%> run_env[<%= key['name'] %>] <%= key['environment_variable'] %> diff --git a/pages/test_engine/dotnet_collectors.md b/pages/test_engine/dotnet_collectors.md index 9067502297..1e0a0f548e 100644 --- a/pages/test_engine/dotnet_collectors.md +++ b/pages/test_engine/dotnet_collectors.md @@ -4,7 +4,7 @@ toc: false # .NET collector -To use Test Analytics with your .NET projects use the :github: [`test-collector-dotnet`](https://github.com/buildkite/test-collector-dotnet) package with xUnit. +To use Test Engine with your .NET projects use the :github: [`test-collector-dotnet`](https://github.com/buildkite/test-collector-dotnet) package with xUnit. You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). @@ -32,4 +32,4 @@ Before you start, make sure .NET runs with access to [CI environment variables]( 1. Verify that it works -If all is well, you should see the test run in the test analytics section of the Buildkite dashboard. +If all is well, you should see the test run in the analytics section of the Buildkite dashboard. diff --git a/pages/test_engine/elixir_collectors.md b/pages/test_engine/elixir_collectors.md index c00de0c2f0..aea3fd308d 100644 --- a/pages/test_engine/elixir_collectors.md +++ b/pages/test_engine/elixir_collectors.md @@ -4,7 +4,7 @@ toc: false # Elixir collectors -To use Test Analytics with your Elixir projects use :github: [`test_collector_elixir`](https://github.com/buildkite/test_collector_elixir) with ExUnit. +To use Test Engine with your Elixir projects use :github: [`test_collector_elixir`](https://github.com/buildkite/test_collector_elixir) with ExUnit. You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). @@ -28,7 +28,7 @@ Before you start, make sure ExUnit runs with access to [CI environment variables 1. Set up your API token: - In your `config/test.exs` (or other environment configuration as appropriate) add the analytics API token. We suggest that you retrieve the token from the environment, and configure your CI environment accordingly (for example using secrets). + In your `config/test.exs` (or other environment configuration as appropriate) add the Buildkite Test Engine API token. We suggest that you retrieve the token from the environment, and configure your CI environment accordingly (for example using secrets). ```elixir import Config @@ -60,4 +60,4 @@ Before you start, make sure ExUnit runs with access to [CI environment variables Randomized with seed 12345 ``` -1. Verify that it works. If all is well, you should see the test run in the test analytics section of the Buildkite dashboard. +1. Verify that it works. If all is well, you should see the test run in the analytics section of the Buildkite dashboard. diff --git a/pages/test_engine/golang_collectors.md b/pages/test_engine/golang_collectors.md index 8a71a5f475..e5cac9fef4 100644 --- a/pages/test_engine/golang_collectors.md +++ b/pages/test_engine/golang_collectors.md @@ -2,9 +2,9 @@ toc: false --- -# Configuring Go with Test Analytics +# Configuring Go with Test Engine -To use Test Analytics with your [Go](https://go.dev/) language projects use [gotestsum](https://github.com/gotestyourself/gotestsum) to generate JUnit XML files, then [upload the JUnit XML files](/docs/test-engine/importing-junit-xml) to Test Analytics. +To use Test Engine with your [Go](https://go.dev/) language projects use [gotestsum](https://github.com/gotestyourself/gotestsum) to generate JUnit XML files, then [upload the JUnit XML files](/docs/test-engine/importing-junit-xml) to Test Engine. 1. Install [gotestsum](https://github.com/gotestyourself/gotestsum): diff --git a/pages/test_engine/importing_json.md b/pages/test_engine/importing_json.md index a681aa65b5..d9c7e5daf7 100644 --- a/pages/test_engine/importing_json.md +++ b/pages/test_engine/importing_json.md @@ -1,16 +1,15 @@ # Importing JSON - -If a test collector is not available for your test framework, you can upload tests results directly to the Test Analytics API or [write your own test collector](/docs/test-engine/your-own-collectors). +If a test collector is not available for your test framework, you can upload tests results directly to the Test Engine API or [write your own test collector](/docs/test-engine/your-own-collectors). You can upload JSON-formatted test results (described in this page) or [JUnit XML](/docs/test-engine/importing-junit-xml). ## How to import JSON in Buildkite -It's possible to import JSON (or [JUnit](/docs/test-engine/importing-junit-xml#how-to-import-junit-xml-in-buildkite) files) to Buildkite Test Analytics with or without the help of a plugin. +It's possible to import JSON (or [JUnit](/docs/test-engine/importing-junit-xml#how-to-import-junit-xml-in-buildkite) files) to Buildkite Test Engine with or without the help of a plugin. ### Using a plugin -To import [JSON-formatted test results](#json-test-results-data-reference) to Test Analytics using [Test Collector plugin](https://github.com/buildkite-plugins/test-collector-buildkite-plugin) from a build step: +To import [JSON-formatted test results](#json-test-results-data-reference) to Test Engine using [Test Collector plugin](https://github.com/buildkite-plugins/test-collector-buildkite-plugin) from a build step: ```yml steps: @@ -35,7 +34,7 @@ To import [JSON-formatted test results](#json-test-results-data-reference) in Bu For example, to import the contents of a [JSON-formatted test results](#json-test-results-data-reference) (`test-results.json`): -1. Securely [set the Test Analytics token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). +1. Securely [set the Test Engine token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). 2. Run the following `curl` command: @@ -66,7 +65,7 @@ To import [JSON-formatted test results](#json-test-results-data-reference), make For example, to import the contents of a `test-results.json` file in a CircleCI pipeline: -1. Securely [set the Test Analytics token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). +1. Securely [set the Test Engine token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). 2. Run the following `curl` command: @@ -95,7 +94,7 @@ To import [JSON-formatted test results](#json-test-results-data-reference), make For example, to import the contents of a `test-results.json` file in a GitHub Actions pipeline run: -1. Securely [set the Test Analytics token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). +1. Securely [set the Test Engine token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). 2. Run the following `curl` command: @@ -186,7 +185,7 @@ end - <% TEST_ANALYTICS_JSON_FIELDS_TEST_RESULT['fields'].each do |field| -%> + <% TEST_ENGINE_JSON_FIELDS_TEST_RESULT['fields'].each do |field| -%> <%= field['name'] %> <%= '(required)' if field['required'] %> <%= field['type'] %> <%= render_enumerated_values(field['enumerated_values']) %> @@ -234,7 +233,7 @@ A failure expanded array contains extra details about the failed test. - <% TEST_ANALYTICS_JSON_FIELDS_FAILURE_EXPANDED['fields'].each do |field| -%> + <% TEST_ENGINE_JSON_FIELDS_FAILURE_EXPANDED['fields'].each do |field| -%> <%= field['name'] %> <%= '(required)' if field['required'] %> <%= field['type'] %> <%= render_enumerated_values(field['enumerated_values']) %> @@ -283,7 +282,7 @@ A history object represents the overall duration of the test run and contains de - <% TEST_ANALYTICS_JSON_FIELDS_HISTORY['fields'].each do |field| -%> + <% TEST_ENGINE_JSON_FIELDS_HISTORY['fields'].each do |field| -%> <%= field['name'] %> <%= '(required)' if field['required'] %> <%= field['type'] %> <%= render_enumerated_values(field['enumerated_values']) %> @@ -322,7 +321,7 @@ It represents, for example, the duration of an individual database query within - <% TEST_ANALYTICS_JSON_FIELDS_SPAN['fields'].each do |field| -%> + <% TEST_ENGINE_JSON_FIELDS_SPAN['fields'].each do |field| -%> <%= field['name'] %> <%= '(required)' if field['required'] %> <%= field['type'] %> <%= render_enumerated_values(field['enumerated_values']) %> @@ -361,7 +360,7 @@ Detail objects contains additional information about the span. - <% TEST_ANALYTICS_JSON_FIELDS_DETAIL['fields'].each do |field| -%> + <% TEST_ENGINE_JSON_FIELDS_DETAIL['fields'].each do |field| -%> <%= field['name'] %> <%= '(required)' if field['required'] %> <%= field['type'] %> <%= render_enumerated_values(field['enumerated_values']) %> diff --git a/pages/test_engine/importing_junit_xml.md b/pages/test_engine/importing_junit_xml.md index 9709105bab..d29c05a03e 100644 --- a/pages/test_engine/importing_junit_xml.md +++ b/pages/test_engine/importing_junit_xml.md @@ -1,6 +1,6 @@ # Importing JUnit XML -While most test frameworks have a built-in JUnit XML export feature, these JUnit reports do not provide detailed span information. Therefore, features in Test Analytics that depend on span information aren't available when using JUnit as a data source. If you need span information, consider using the [JSON import](/docs/test-engine/importing-json) API instead. +While most test frameworks have a built-in JUnit XML export feature, these JUnit reports do not provide detailed span information. Therefore, features in Test Engine that depend on span information aren't available when using JUnit as a data source. If you need span information, consider using the [JSON import](/docs/test-engine/importing-json) API instead. ## Mandatory JUnit XML attributes @@ -15,11 +15,11 @@ To learn more about the JUnit XML file format, see [Common JUnit XML format & ex ## How to import JUnit XML in Buildkite -It's possible to import XML-formatted JUnit (or [JSON](/docs/test-engine/importing-json#how-to-import-json-in-buildkite)) test results to Buildkite Test Analytics with or without the help of a plugin. +It's possible to import XML-formatted JUnit (or [JSON](/docs/test-engine/importing-json#how-to-import-json-in-buildkite)) test results to Buildkite Test Engine with or without the help of a plugin. ### Using a plugin -To import XML-formatted JUnit test results to Test Analytics using [Test Collector plugin](https://github.com/buildkite-plugins/test-collector-buildkite-plugin) from a build step: +To import XML-formatted JUnit test results to Test Engine using [Test Collector plugin](https://github.com/buildkite-plugins/test-collector-buildkite-plugin) from a build step: ```yml steps: @@ -40,10 +40,10 @@ Using the plugin is the recommended way as it allows for a better debugging proc If for some reason you cannot or do not want to use the [Test Collector plugin](https://github.com/buildkite-plugins/test-collector-buildkite-plugin), or if you are looking to implement your own integration, another approach is possible. -To import XML-formatted JUnit test results to Test Analytics, make a `POST` request to `https://analytics-api.buildkite.com/v1/uploads` with a `multipart/form-data`. +To import XML-formatted JUnit test results to Test Engine, make a `POST` request to `https://analytics-api.buildkite.com/v1/uploads` with a `multipart/form-data`. For example, to import the contents of a `junit.xml` file in a Buildkite pipeline: -1. Securely [set the Test Analytics token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). +1. Securely [set the Test Engine token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). 1. Run the following `curl` command: @@ -77,7 +77,7 @@ A single file can have a maximum of 5000 test results, and if that limit is exce To import XML-formatted JUnit test results, make a `POST` request to `https://analytics-api.buildkite.com/v1/uploads` with a `multipart/form-data`. For example, to import the contents of a `junit.xml` file in a CircleCI pipeline: -1. Securely [set the Test Analytics token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). +1. Securely [set the Test Engine token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). 1. Run the following `curl` command: @@ -109,7 +109,7 @@ A single file can have a maximum of 5000 test results, and if that limit is exce To import XML-formatted JUnit test results, make a `POST` request to `https://analytics-api.buildkite.com/v1/uploads` with a `multipart/form-data`. For example, to import the contents of a `junit.xml` file in a GitHub Actions pipeline: -1. Securely [set the Test Analytics token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). +1. Securely [set the Test Engine token environment variable](/docs/pipelines/security/secrets/managing) (`BUILDKITE_ANALYTICS_TOKEN`). 1. Run the following `curl` command: diff --git a/pages/test_engine/javascript_collectors.md b/pages/test_engine/javascript_collectors.md index 3227401d70..fd2c4ffd89 100644 --- a/pages/test_engine/javascript_collectors.md +++ b/pages/test_engine/javascript_collectors.md @@ -1,6 +1,6 @@ # JavaScript collectors -To use Test Analytics with your JavaScript (npm) projects, use the :github: [`test-collector-javascript`](https://github.com/buildkite/test-collector-javascript) package with a supported test framework. Test Analytics supports the following test frameworks: +To use Test Engine with your JavaScript (npm) projects, use the :github: [`test-collector-javascript`](https://github.com/buildkite/test-collector-javascript) package with a supported test framework. Test Engine supports the following test frameworks: - [Jest](https://jestjs.io/) - [Jasmine](https://jasmine.github.io/) @@ -17,13 +17,13 @@ Whichever test framework you use, you first need to add and authenticate the [`b To add the test collector package: -1. In your CI environment, set the `BUILDKITE_ANALYTICS_TOKEN` environment variable to your Test Analytics API token. +1. In your CI environment, set the `BUILDKITE_ANALYTICS_TOKEN` environment variable to your Test Engine API token. To learn how to set environment variables securely in Pipelines, see [Managing pipeline secrets](/docs/pipelines/security/secrets/managing). 1. On the command line, create a new branch by running: ``` - git checkout -b install-buildkite-test-analytics + git checkout -b install-buildkite-test-engine ``` 1. Install [`buildkite-test-collector`](https://www.npmjs.com/package/buildkite-test-collector) using your package manager. @@ -46,7 +46,7 @@ With the test collector installed, you need to configure it in the test framewor ### Jest -If you're already using Jest, you can add `buildkite-test-collector/jest/reporter` to the list of reporters to collect test results into your Test Analytics dashboard. +If you're already using Jest, you can add `buildkite-test-collector/jest/reporter` to the list of reporters to collect test results into your Test Engine dashboard. To configure Jest: @@ -59,7 +59,7 @@ To configure Jest: "testLocationInResults": true, } ``` - **Note:** The `"testLocationInResults": true` setting enables column and line capture for Test Analytics. + **Note:** The `"testLocationInResults": true` setting enables column and line capture for Test Engine. ### Jasmine @@ -133,7 +133,7 @@ To configure Cypress: ```js // cypress.config.js - // Send results to Test Analytics + // Send results to Test Engine reporter: "buildkite-test-collector/cypress/reporter", ``` @@ -142,7 +142,7 @@ To configure Cypress: ```js // cypress.config.js - // Send results to Test Analytics + // Send results to Test Engine reporterOptions: { token_name: "CUSTOM_ENV_VAR_NAME" } @@ -150,7 +150,7 @@ To configure Cypress: ### Playwright -If you're already using Playwright, you can add `buildkite-test-collector/playwright/reporter` to the list of reporters to collect test results into your Test Analytics dashboard. +If you're already using Playwright, you can add `buildkite-test-collector/playwright/reporter` to the list of reporters to collect test results into your Test Engine dashboard. To configure Playwright: @@ -180,7 +180,7 @@ When your collector is installed, commit and push your changes: 1. Commit the changes by running: ```shell - git commit -m "Install and set up Buildkite Test Analytics" + git commit -m "Install and set up Buildkite Test Engine" ``` 1. Push the changes by running: @@ -191,13 +191,13 @@ When your collector is installed, commit and push your changes: ## View the results -After completing these steps, you'll see the analytics of test executions on all branches that include this code in the Test Analytics dashboard. +After completing these steps, you'll see the analytics of test executions on all branches that include this code in the Test Engine dashboard. -If you don't see branch names, build numbers, or commit hashes in the Test Analytics dashboard, see [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment. +If you don't see branch names, build numbers, or commit hashes in the Test Engine dashboard, see [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment. ## Troubleshooting missing test executions and --forceExit -Using the [`--forceExit`](https://jestjs.io/docs/cli#--forceexit) option when running Jest could result in missing test executions from Test Analytics. +Using the [`--forceExit`](https://jestjs.io/docs/cli#--forceexit) option when running Jest could result in missing test executions from Test Engine. `--forceExit` could potentially terminate any ongoing processes that are attempting to send test executions to Buildkite. diff --git a/pages/test_engine/other_collectors.md b/pages/test_engine/other_collectors.md index 54a371f7ff..16c1ba20b4 100644 --- a/pages/test_engine/other_collectors.md +++ b/pages/test_engine/other_collectors.md @@ -4,4 +4,4 @@ toc: false # Collecting data from other test frameworks -You can integrate any language and framework by uploading [Test Analytics JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml) after your tests run. You can also [build your own collector](/docs/test-engine/your-own-collectors). +You can integrate any language and framework by uploading [Test Engine JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml) after your tests run. You can also [build your own collector](/docs/test-engine/your-own-collectors). diff --git a/pages/test_engine/permissions.md b/pages/test_engine/permissions.md index 96c348c569..ee999c19f9 100644 --- a/pages/test_engine/permissions.md +++ b/pages/test_engine/permissions.md @@ -6,7 +6,7 @@ Enterprise customers can configure test suite permissions and security features ## Manage teams and permissions -To manage teams across the Buildkite Test Analytics application, a _Buildkite organization administrator_ first needs to enable this feature across their organization. Learn more about how to do this in the [Manage teams and permissions section of Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions). +To manage teams across the Buildkite Test Engine application, a _Buildkite organization administrator_ first needs to enable this feature across their organization. Learn more about how to do this in the [Manage teams and permissions section of Pipelines documentation](/docs/team-management/permissions#manage-teams-and-permissions). Once the _teams_ feature is enabled, you can see the teams that you're a member of from the **User** page, which: @@ -70,7 +70,7 @@ These user-level permissions and security features are managed by _Buildkite org 1. Select **Settings** in the global navigation to access the [**Organization Settings**](https://buildkite.com/organizations/~/settings) page. -1. Select [**Security** > **Test Analytics** tab](https://buildkite.com/organizations/~/security/test-analytics) to access your organization's security for Test Analytics page. +1. Select [**Security** > **Test Engine** tab](https://buildkite.com/organizations/~/security/test-analytics) to access your organization's security for Test Engine page. From this page, you can configure the following permissions for all users across your Buildkite organization: diff --git a/pages/test_engine/python_collectors.md b/pages/test_engine/python_collectors.md index 5fba392136..48b741eb9c 100644 --- a/pages/test_engine/python_collectors.md +++ b/pages/test_engine/python_collectors.md @@ -4,20 +4,20 @@ toc: false # Python collectors -To use Test Analytics with your Python projects use the [`buildkite-test-collector`](https://pypi.org/project/buildkite-test-collector/) package with pytest. +To use Test Engine with your Python projects use the [`buildkite-test-collector`](https://pypi.org/project/buildkite-test-collector/) package with pytest. You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). ## pytest collector pytest is a testing framework for Python. -If you're already using pytest, then you can install `buildkite-test-collector` to collect test results into your Test Analytics dashboard. +If you're already using pytest, then you can install `buildkite-test-collector` to collect test results into your Test Engine dashboard. Before you start, make sure pytest runs with access to [CI environment variables](/docs/test-engine/ci-environments). To get started with `buildkite-test-collector`: -1. In your CI environment, set the `BUILDKITE_ANALYTICS_TOKEN` environment variable [securely](/docs/pipelines/security/secrets/managing) to your Buildkite Test Analytics API token. +1. In your CI environment, set the `BUILDKITE_ANALYTICS_TOKEN` environment variable [securely](/docs/pipelines/security/secrets/managing) to your Buildkite Test Engine API token. 1. Add `buildkite-test-collector` to your list of dependencies. Some examples: @@ -48,10 +48,10 @@ To get started with `buildkite-test-collector`: ```shell $ git add . - $ git commit -m "Install and set up Buildkite Test Analytics" + $ git commit -m "Install and set up Buildkite Test Engine" $ git push ``` -Once you're done, in your Test Analytics dashboard, you'll see analytics of test executions on all branches that include this code. +Once you're done, in your Test Engine dashboard, you'll see analytics of test executions on all branches that include this code. -If you don't see branch names, build numbers, or commit hashes in Test Analytics, then read [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. +If you don't see branch names, build numbers, or commit hashes in Test Engine, then read [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. diff --git a/pages/test_engine/ruby_collectors.md b/pages/test_engine/ruby_collectors.md index 8c67f82db0..fea26b81d1 100644 --- a/pages/test_engine/ruby_collectors.md +++ b/pages/test_engine/ruby_collectors.md @@ -1,6 +1,6 @@ # Ruby collectors -To use Test Analytics with your [Ruby](https://www.ruby-lang.org/) projects use the :github: [`test-collectors-ruby`](https://github.com/buildkite/test-collector-ruby) gem with RSpec or minitest. +To use Test Engine with your [Ruby](https://www.ruby-lang.org/) projects use the :github: [`test-collectors-ruby`](https://github.com/buildkite/test-collector-ruby) gem with RSpec or minitest. You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). @@ -8,14 +8,14 @@ You can also upload test results by importing [JSON](/docs/test-engine/importing ## RSpec collector [RSpec](https://rspec.info/) is a behavior-driven development library for Ruby. -If you're already using RSpec for your tests, add the `buildkite-test_collector` gem to your code to collect your test results into your Test Analytics dashboard. +If you're already using RSpec for your tests, add the `buildkite-test_collector` gem to your code to collect your test results into your Test Engine dashboard. Before you start, make sure RSpec runs with access to [CI environment variables](/docs/test-engine/ci-environments). 1. Create a new branch: ``` - git checkout -b install-buildkite-test-analytics + git checkout -b install-buildkite-test-engine ``` 2. Add `buildkite-test_collector` to your `Gemfile` in the `:test` group: @@ -32,7 +32,7 @@ Before you start, make sure RSpec runs with access to [CI environment variables] bundle ``` -3. Add the Test Analytics code to your application in `spec/spec_helper.rb`, and set the BUILDKITE_ANALYTICS_TOKEN [securely](/docs/pipelines/security/secrets/managing) on your agent or agents. Please ensure gems that patch `Net::HTTP`, like [httplog](https://github.com/trusche/httplog) and [sniffer](https://github.com/aderyabin/sniffer), are required before `buildkite/test_collector` to avoid conflicts. +3. Add the Test Engine code to your application in `spec/spec_helper.rb`, and set the BUILDKITE_ANALYTICS_TOKEN [securely](/docs/pipelines/security/secrets/managing) on your agent or agents. Please ensure gems that patch `Net::HTTP`, like [httplog](https://github.com/trusche/httplog) and [sniffer](https://github.com/aderyabin/sniffer), are required before `buildkite/test_collector` to avoid conflicts. ```rb require "buildkite/test_collector" @@ -44,13 +44,13 @@ Before you start, make sure RSpec runs with access to [CI environment variables] ```sh $ git add . - $ git commit -m "Install and set up Buildkite Test Analytics" + $ git commit -m "Install and set up Buildkite Test Engine" $ git push ``` -Once you're done, in your Test Analytics dashboard, you'll see analytics of test executions on all branches that include this code. +Once you're done, in your Test Engine dashboard, you'll see analytics of test executions on all branches that include this code. -If you don't see branch names, build numbers, or commit hashes in the Test Analytics UI, then see [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. +If you don't see branch names, build numbers, or commit hashes in the Test Engine UI, then see [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. ### Troubleshooting allow_any_instance_of errors @@ -67,15 +67,15 @@ You can fix them by being more specific in your stubbing by replacing `allow_any RSpec supports anonymous test cases—tests which are automatically named based on the subject and/or inputs to the expectations within the test. However, this can lead to unstable test names across different test runs, incorporating elements such as object IDs, database IDs, timestamps, and more. -As a consequence, each test is assigned a new identity per run within Test Analytics. This poses a challenge for utilising the Test Analytics product effectively, as historical data across tests becomes difficult to track and analyze. +As a consequence, each test is assigned a new identity per run within Test Engine. This poses a challenge for using the Test Engine product effectively, as historical data across tests becomes difficult to track and analyze. -To mitigate this issue and ensure the reliability of Test Analytics, it's advisable to provide explicit and stable descriptions for each test case within your RSpec test suite. By doing so, you can maintain consistency in test identification across multiple runs, enabling better tracking and analysis of test performance over time. +To mitigate this issue and ensure the reliability of Test Engine, it's advisable to provide explicit and stable descriptions for each test case within your RSpec test suite. By doing so, you can maintain consistency in test identification across multiple runs, enabling better tracking and analysis of test performance over time. ## minitest collector [minitest](https://github.com/minitest/minitest) provides a complete suite of testing facilities supporting TDD, BDD, mocking, and benchmarking. -If you're already using minitest for your tests, add the `buildkite-test_collector` gem to your code to collect your test results into your Test Analytics dashboard. +If you're already using minitest for your tests, add the `buildkite-test_collector` gem to your code to collect your test results into your Test Engine dashboard. 1. Create a new branch: @@ -97,7 +97,7 @@ If you're already using minitest for your tests, add the `buildkite-test_collect bundle ``` -3. Add the Test Analytics code to your application in `test/test_helper.rb`, and set the BUILDKITE_ANALYTICS_TOKEN [securely](/docs/pipelines/security/secrets/managing) on your agent or agents. Please ensure gems that patch `Net::HTTP`, like [httplog](https://github.com/trusche/httplog) and [sniffer](https://github.com/aderyabin/sniffer), are required before `buildkite/test_collector` to avoid conflicts. +3. Add the Test Engine code to your application in `test/test_helper.rb`, and set the BUILDKITE_ANALYTICS_TOKEN [securely](/docs/pipelines/security/secrets/managing) on your agent or agents. Please ensure gems that patch `Net::HTTP`, like [httplog](https://github.com/trusche/httplog) and [sniffer](https://github.com/aderyabin/sniffer), are required before `buildkite/test_collector` to avoid conflicts. ```rb require "buildkite/test_collector" @@ -109,13 +109,13 @@ If you're already using minitest for your tests, add the `buildkite-test_collect ```sh git add . - git commit -m "Install and set up Buildkite Test Analytics" + git commit -m "Install and set up Buildkite Test Engine" git push ``` -Once you're done, in your Test Analytics dashboard, you'll see analytics of test executions on all branches that include this code. +Once you're done, in your Test Engine dashboard, you'll see analytics of test executions on all branches that include this code. -If you don't see branch names, build numbers, or commit hashes in the Test Analytics UI, then see [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the minitest collector. +If you don't see branch names, build numbers, or commit hashes in the Test Engine UI, then see [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the minitest collector. ## Adding annotation spans diff --git a/pages/test_engine/rust_collectors.md b/pages/test_engine/rust_collectors.md index 3b1f4117e5..7ba41b66d6 100644 --- a/pages/test_engine/rust_collectors.md +++ b/pages/test_engine/rust_collectors.md @@ -4,7 +4,7 @@ toc: false # Rust collector -To use Test Analytics with your [Rust](https://www.rust-lang.org/) projects use the :github: [`test-collector-rust`](https://github.com/buildkite/test-collector-rust) package with `cargo test`. +To use Test Engine with your [Rust](https://www.rust-lang.org/) projects use the :github: [`test-collector-rust`](https://github.com/buildkite/test-collector-rust) package with `cargo test`. You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). @@ -34,4 +34,4 @@ Before you start, make sure Rust runs with access to [CI environment variables]( $ cargo test -- -Z unstable-options --format json --report-time | buildkite-test-collector ``` -1. Confirm correct operation. Verify that the run is visible in the Buildkite analytics dashboard. +1. Confirm correct operation. Verify that the run is visible in the Buildkite Test Engine dashboard. diff --git a/pages/test_engine/swift_collectors.md b/pages/test_engine/swift_collectors.md index dfa125e647..8fac43b371 100644 --- a/pages/test_engine/swift_collectors.md +++ b/pages/test_engine/swift_collectors.md @@ -4,7 +4,7 @@ toc: false # Swift collectors -To use Test Analytics with your Swift projects use the :github: [`test-collector-swift`](https://github.com/buildkite/test-collector-swift) package with XCTest. +To use Test Engine with your Swift projects use the :github: [`test-collector-swift`](https://github.com/buildkite/test-collector-swift) package with XCTest. You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). @@ -49,14 +49,14 @@ Before you start, make sure XCTest runs with access to [CI environment variables 1. Commit and push your changes: ```bash - git checkout -b add-buildkite-test-analytics - git commit -am "Add Buildkite Test Analytics" - git push origin add-buildkite-test-analytics + git checkout -b add-buildkite-test-engine + git commit -am "Add Buildkite Test Engine" + git push origin add-buildkite-test-engine ``` -Once you're done, in your Test Analytics dashboard, you'll see analytics of test executions on all branches that include this code. +Once you're done, in your Test Engine dashboard, you'll see analytics of test executions on all branches that include this code. -If you don't see branch names, build numbers, or commit hashes in Test Analytics, then read [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. +If you don't see branch names, build numbers, or commit hashes in Test Engine, then read [CI environments](/docs/test-engine/ci-environments) to learn more about exporting your environment to the collector. ### Debugging diff --git a/pages/test_engine/test_ownership.md b/pages/test_engine/test_ownership.md index 006cd751f2..9adadda3c0 100644 --- a/pages/test_engine/test_ownership.md +++ b/pages/test_engine/test_ownership.md @@ -19,7 +19,7 @@ A TESTOWNERS file uses Buildkite team slugs instead of user names. Your team slu ```bash # Example team name to slug Pipelines => pipelines -Test Analytics => test-analytics +Test Engine => test-engine 📦 Packages => packages ``` @@ -49,8 +49,8 @@ The following example TESTOWNERS file, which you can copy as a starting point, e * team-slug-1 team-slug-2 # In this example, any test file ending with `_spec.rb` will be -# assigned to the `test-analytics` team and not `team-slug-1`. -*_spec.rb test-analytics # This is an inline comment. +# assigned to the `test-engine` team and not `team-slug-1`. +*_spec.rb test-engine # This is an inline comment. # In this example, the `pipelines` team owns all `.rb` test files. *.rb pipelines @@ -64,18 +64,18 @@ The following example TESTOWNERS file, which you can copy as a starting point, e # like `spec/features/application_spec.rb`, but not any test files # nested in any subdirectories of `spec/features`, such as # `spec/features/pipelines/application_spec.rb`. -spec/features/* test-analytics +spec/features/* test-engine # In this example, the `pipelines` team owns any test file in any # `pipelines` directory, anywhere within the test location. pipelines/ pipelines -# In this example, the `test-analytics` team owns any test files -# within an `/analytics` directory such as `/models/analytics`, -# `/features/analytics`, and `/models/organizations/analytics`. -# Any test files directly within the `/analytics` directory itself -# will also belong to the `test-analytics` team. -**/analytics test-analytics +# In this example, the `test-engine` team owns any test files +# within an `/test-engine` directory such as `/models/test-engine`, +# `/features/test-engine`, and `/models/organizations/test-engine`. +# Any test files directly within the `/test-engine` directory itself +# will also belong to the `test-engine` team. +**/test-engine test-engine # In this example, the `pipelines` team owns any test files in the # `/spec` directory at the root of the test location. However, the @@ -117,15 +117,15 @@ A TESTOWNERS file [follows the same rules as a `.gitignore` or `CODEOWNERS` file ```bash # In a regular `.gitignore` or `CODEOWNER` file, the following -# block would set the `test-analytics` team as the owner of any +# block would set the `test-engine` team as the owner of any # file in the `/specs` directory at the root of your test location # except for the `/specs/features` subdirectory, as its owners are # left empty. # This functionality is not supported in a Buildkite `TESTOWNERS` # file, where `/spec/features` would be also be owned by the -# `test-analytics` team. +# `test-engine` team. -/specs/ test-analytics +/specs/ test-engine /specs/features ``` diff --git a/pages/test_engine/test_splitting.md b/pages/test_engine/test_splitting.md index da78ad0ac3..f80ce28693 100644 --- a/pages/test_engine/test_splitting.md +++ b/pages/test_engine/test_splitting.md @@ -1,6 +1,6 @@ # Configuring test splitting -Test splitting is the process of partitioning a test suite to run in parallel across multiple Buildkite agents. Buildkite maintains its open source Test Splitter ([test-splitter](https://github.com/buildkite/test-splitter)) tool. This tool uses your Buildkite Test Analytics test suite data to intelligently partition tests throughout your test suite into multiple sets, such that each set of tests runs in parallel across your agents. This process is known as _orchestration_ and results in a _test plan_, where a test plan defines which tests are run on which agents. Currently, the Test Splitter tool only supports RSpec. +Test splitting is the process of partitioning a test suite to run in parallel across multiple Buildkite agents. Buildkite maintains its open source Test Splitter ([test-splitter](https://github.com/buildkite/test-splitter)) tool. This tool uses your Buildkite Test Engine test suite data to intelligently partition tests throughout your test suite into multiple sets, such that each set of tests runs in parallel across your agents. This process is known as _orchestration_ and results in a _test plan_, where a test plan defines which tests are run on which agents. Currently, the Test Splitter tool only supports RSpec. ## Dependencies diff --git a/pages/test_engine/test_suites.md b/pages/test_engine/test_suites.md index 56eeb8916b..5ffdee728a 100644 --- a/pages/test_engine/test_suites.md +++ b/pages/test_engine/test_suites.md @@ -1,16 +1,16 @@ # Configuring test suites -In Test Analytics, a test _suite_ is a collection of tests. A suite has a _run_, which is the execution of tests in a suite. A suite's run is analogous to a pipeline's build. +In Test Engine, a test _suite_ is a collection of tests. A suite has a _run_, which is the execution of tests in a suite. A suite's run is analogous to a pipeline's build. Many organizations set up one suite per test framework, for example one suite for RSpec, and another suite for Jest. Others use a common standard, such as JUnit XML, to combine tests from multiple frameworks to set up custom backend and frontend suites. -Each suite inside Test Analytics has a unique API token that you can use to route test information to the correct suite. Pipelines and test suites do not need to have a one-to-one relationship. +Each suite inside Test Engine has a unique API token that you can use to route test information to the correct suite. Pipelines and test suites do not need to have a one-to-one relationship. To delete a suite, or regenerate its API token, go to suite settings. ## Parallelized builds -Test Analytics works even when your test runs are split across different agents by de-duplicating against the Test Analytics API token and unique build identifier. +Test Engine works even when your test runs are split across different agents by de-duplicating against the Test Engine API token and unique build identifier. The information that serves as a unique build identifier differs between CI environments. For details, see `run_env[key]` environment variables on our [CI environments page](/docs/test-engine/ci-environments). @@ -20,23 +20,23 @@ All test suites have a default branch so you can track trends for your most impo Organizations typically choose their main production branch as their default, although this is not required. -To change your default branch, go to suite settings. You can also filter Test Analytics views by any branch by typing its name into the branch query parameter in the Test Analytics URL. +To change your default branch, go to suite settings. You can also filter Test Engine views by any branch by typing its name into the branch query parameter in the Test Engine URL. ## Detecting flaky tests Flaky tests are automated tests that produce inconsistent or unreliable results, despite being run on the same code and environment. They cause frustration, decrease confidence in testing, and waste time while you investigate whether the failure is due to a genuine bug. -Test Analytics detects flaky tests by surfacing when the same test is run multiple times on the same commit SHA with different results. The tests might run multiple times within a single build or across different builds. Either way, they are detected as flaky if they report both passed and failed results. +Test Engine detects flaky tests by surfacing when the same test is run multiple times on the same commit SHA with different results. The tests might run multiple times within a single build or across different builds. Either way, they are detected as flaky if they report both passed and failed results. -If your test suite supports it, we recommend enabling the option to retry failed tests automatically. Automatic retries are typically run more often and provide more data to detect flaky tests. If you can't use automatic retries, Test Analytics also detects flaky tests from manual retries. +If your test suite supports it, we recommend enabling the option to retry failed tests automatically. Automatic retries are typically run more often and provide more data to detect flaky tests. If you can't use automatic retries, Test Engine also detects flaky tests from manual retries. Alternatively, you can create [scheduled builds](/docs/pipelines/scheduled-builds) to run your test suite on the default branch. You can schedule them outside your typical development time to run the test suite multiple times against the same commit SHA. You can still enable test retries in this setup, but they're less important. The more builds you run, the more likely you'll detect flaky tests that fail infrequently. -Test Analytics reviews the test results to detect flaky tests after every test run. +Test Engine reviews the test results to detect flaky tests after every test run. ## Tracking reliability -Test Analytics calculates reliability of both your entire test suite and individual tests as a measure of flakiness over time. +Test Engine calculates reliability of both your entire test suite and individual tests as a measure of flakiness over time. _Reliability_ is defined as percentage calculated by: @@ -45,7 +45,7 @@ _Reliability_ is defined as percentage calculated by: Other test execution results such as `unknown` and `skipped` are ignored in the test reliability calculation. -In Test Analytics, a run is marked as `failed` as soon as a test execution fails, regardless of whether it passes on a retry. This helps surface unreliable tests. You can have a situation where a build eventually passes on retry in a Pipeline, and the related run is marked as `failed` in Test Analytics. +In Test Engine, a run is marked as `failed` as soon as a test execution fails, regardless of whether it passes on a retry. This helps surface unreliable tests. You can have a situation where a build eventually passes on retry in a Pipeline, and the related run is marked as `failed` in Test Engine. ## Trends and analysis @@ -59,6 +59,6 @@ Select any individual test execution to see more trend and deep-dive information <%= image "test-execution-stats.png", width: 1170, height: 578, alt: "Screenshot of individual test execution page showing test information related to that individual execution of the test" %> -You can also annotate span information to help investigate problems, and see detailed log information inside Test Analytics for any failed test or run. +You can also annotate span information to help investigate problems, and see detailed log information inside Test Engine for any failed test or run. <%= image "span-timeline.png", width: 1125, height: 451, alt: "Screenshot of span timeline with user-defined annotation" %> diff --git a/pages/test_engine/your_own_collectors.md b/pages/test_engine/your_own_collectors.md index 208854dcca..9726ed2999 100644 --- a/pages/test_engine/your_own_collectors.md +++ b/pages/test_engine/your_own_collectors.md @@ -4,6 +4,6 @@ toc: false # Your own collectors -Test Analytics integrates directly with your test runner to provide in-depth information about your tests (including spans) in real time. +Test Engine integrates directly with your test runner to provide in-depth information about your tests (including spans) in real time. -If you're interested in building your own fully integrated analytics collector for specific test runners, have a look at our [example collector](https://github.com/buildkite/rspec-buildkite-analytics) on GitHub. +If you're interested in building your own fully integrated Test Engine collector for specific test runners, have a look at our [example collector](https://github.com/buildkite/rspec-buildkite-analytics) on GitHub. From 46f55c3e986b5c63c317e7cf4d750c8d78d1dd40 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 16 Sep 2024 11:55:01 +1000 Subject: [PATCH 123/369] Implement TA > TE rebrand heading exception. --- vale/styles/Buildkite/h1-h6_sentence_case.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vale/styles/Buildkite/h1-h6_sentence_case.yml b/vale/styles/Buildkite/h1-h6_sentence_case.yml index 97eff668e4..ee02c2bea3 100644 --- a/vale/styles/Buildkite/h1-h6_sentence_case.yml +++ b/vale/styles/Buildkite/h1-h6_sentence_case.yml @@ -147,7 +147,7 @@ exceptions: - S3 - SSH - SSO - - Test Analytics + - Test Engine - TESTOWNER - Ubuntu - URL From 4cc2fb0f606b6581dbf13a26e7df2d0524f602a9 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 16 Sep 2024 13:21:01 +1000 Subject: [PATCH 124/369] Fix TA > TE branding elsewhere throughout the Buildkite Docs. --- pages/pipelines/migrate_from_jenkins.md | 2 +- pages/pipelines/security/audit_log.md | 4 ++-- pages/pipelines/security/secrets/managing.md | 2 +- spec/helpers/application_helper_spec.rb | 8 ++++---- spec/models/page/renderer_spec.rb | 8 ++++---- spec/scripts/graphql_api_content/nav_data_spec.rb | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pages/pipelines/migrate_from_jenkins.md b/pages/pipelines/migrate_from_jenkins.md index 338d01d487..ec7656f103 100644 --- a/pages/pipelines/migrate_from_jenkins.md +++ b/pages/pipelines/migrate_from_jenkins.md @@ -200,4 +200,4 @@ Remember that it may take some time to adapt to the new platform, and be prepare If you need further assistance or have any questions, please don't hesitate to reach out to [support](https://buildkite.com/support). We're here to help you use Buildkite to build your dream CI/CD workflows. -After configuring Buildkite Pipelines for your team, you could get actionable insights from the tests running in pipelines using [Test Analytics](/docs/test-engine). +After configuring Buildkite Pipelines for your team, you could get actionable insights from the tests running in pipelines using [Test Engine](/docs/test-engine). diff --git a/pages/pipelines/security/audit_log.md b/pages/pipelines/security/audit_log.md index 336b49a15c..24dd5c30dd 100644 --- a/pages/pipelines/security/audit_log.md +++ b/pages/pipelines/security/audit_log.md @@ -158,7 +158,7 @@ TEAM_REGISTRY_UPDATED TEAM_REGISTRY_DELETED ``` -#### For Buildkite Test Analytics +#### For Buildkite Test Engine ``` TEAM_SUITE_CREATED @@ -192,7 +192,7 @@ SCM_PIPELINE_SETTINGS_DELETED SCM_PIPELINE_SETTINGS_UPDATED ``` -### Test Analytics +### Test Engine ``` SUITE_API_TOKEN_REGENERATED_EVENT diff --git a/pages/pipelines/security/secrets/managing.md b/pages/pipelines/security/secrets/managing.md index 3aef4d409f..ea998a4f78 100644 --- a/pages/pipelines/security/secrets/managing.md +++ b/pages/pipelines/security/secrets/managing.md @@ -36,7 +36,7 @@ By default, the `environment` hook file is stored in the agent's `hooks` directo The path to this directory varies by platform; read the [installation instructions](/docs/agent/v3/installation) for the path on your platform. The path can also be overridden by the [`hooks-path`](/docs/agent/v3/hooks#hook-locations-agent-hooks) setting. -For example, to expose a Test Analytics API token to a specific pipeline, create an `environment` script in your agent's `hooks` directory that checks for the pipeline slug before exporting the secret: +For example, to expose a Test Engine API token to a specific pipeline, create an `environment` script in your agent's `hooks` directory that checks for the pipeline slug before exporting the secret: ```bash #!/bin/bash diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index f38e61baa6..6f1611e531 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -36,15 +36,15 @@ context "when the path contains dashes" do it "replaces dashes with spaces" do - path = "test-analytics/importing-junit-xml" + path = "test-engine/importing-junit-xml" - expect(top_level_nav_item_name(path)).to eq("Test Analytics") + expect(top_level_nav_item_name(path)).to eq("Test Engine") end it "titleizes" do - path = "test-analytics/importing-junit-xml" + path = "test-engine/importing-junit-xml" - expect(top_level_nav_item_name(path)).to eq("Test Analytics") + expect(top_level_nav_item_name(path)).to eq("Test Engine") end end end diff --git a/spec/models/page/renderer_spec.rb b/spec/models/page/renderer_spec.rb index 29b58fc49d..3192c91383 100644 --- a/spec/models/page/renderer_spec.rb +++ b/spec/models/page/renderer_spec.rb @@ -99,11 +99,11 @@ context "non-external links" do it "does not affect /docs/ links" do md = <<~MD - [Test Analytics](/docs/test-analytics) + [Test Engine](/docs/test-engine) MD html = <<~HTML -

Test Analytics

+

Test Engine

HTML expect(Page::Renderer.render(md).strip).to eql(html.strip) @@ -111,7 +111,7 @@ it "does not affect links to Buildkite domains" do md = <<~MD - [Test Analytics](https://buildkite.com/test-analytics) + [Test Engine](https://buildkite.com/test-engine) [GraphQL Explorer](https://graphql.buildkite.com/explorer) @@ -119,7 +119,7 @@ MD html = <<~HTML -

Test Analytics

+

Test Engine

GraphQL Explorer

diff --git a/spec/scripts/graphql_api_content/nav_data_spec.rb b/spec/scripts/graphql_api_content/nav_data_spec.rb index 771ca40774..407f69684a 100644 --- a/spec/scripts/graphql_api_content/nav_data_spec.rb +++ b/spec/scripts/graphql_api_content/nav_data_spec.rb @@ -10,8 +10,8 @@ "path" => "pipelines" }, { - "name" => "Test Analytics", - "path" => "test-analytics", + "name" => "Test Engine", + "path" => "test-engine", }, { "name" => "APIs", From 9b814de629c3e078d9f4dba309b32c338998cbc0 Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Fri, 13 Sep 2024 16:27:43 +1000 Subject: [PATCH 125/369] ensure deploy depends on prepare --- .buildkite/pipeline.preview.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.buildkite/pipeline.preview.yml b/.buildkite/pipeline.preview.yml index 5c17bae6a3..04a5cf51e2 100644 --- a/.buildkite/pipeline.preview.yml +++ b/.buildkite/pipeline.preview.yml @@ -1,9 +1,11 @@ steps: - label: "Prepare preview" command: bin/prepare-preview + key: prepare-preview - label: "Deploy preview" command: bin/deploy-preview + depends_on: prepare-preview env: RAILS_ENV: "production" plugins: From fa7fb6c1a344ec6a901c987cad38ab3cdd65d3e8 Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Fri, 13 Sep 2024 16:33:33 +1000 Subject: [PATCH 126/369] Parameterize github repo for docs preview --- .buildkite/pipeline.preview.yml | 1 + bin/utils.sh | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.buildkite/pipeline.preview.yml b/.buildkite/pipeline.preview.yml index 04a5cf51e2..ababc6c7e7 100644 --- a/.buildkite/pipeline.preview.yml +++ b/.buildkite/pipeline.preview.yml @@ -14,6 +14,7 @@ steps: dependencies: false mount-buildkite-agent: true env: + - GH_REPO - GH_TOKEN - NETLIFY_AUTH_TOKEN - NETLIFY_SITE_ID diff --git a/bin/utils.sh b/bin/utils.sh index 6e2ddb5c34..371957d47f 100644 --- a/bin/utils.sh +++ b/bin/utils.sh @@ -5,13 +5,19 @@ # Why not `$BUILDKITE_PULL_REQUEST`? That env variable is only set # for builds triggered via Github and it's possible previews are # manually triggered via the API or Buildkite Dashboard. -# + +# ensure GH_REPO env var is present +if [ -z "$GH_REPO" ]; then + echo "GH_REPO env var is required" + exit 1 +fi + function get_branch_pull_request_number() { curl -L \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GH_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/repos/buildkite/docs/pulls\?head=buildkite\:$1 \ + https://api.github.com/repos/${GH_REPO}/pulls\?head=buildkite\:$1 \ | jq ".[0].number | select (.!=null)" } @@ -20,7 +26,7 @@ function find_github_comment() { -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GH_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/repos/buildkite/docs/issues/$1/comments \ + https://api.github.com/repos/${GH_REPO}/issues/$1/comments \ | jq --arg msg "$2" '.[] | select(.body==$msg)' } @@ -31,7 +37,7 @@ function post_github_comment() { -H "Authorization: Bearer $GH_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ --data "{\"body\":\"$2\"}" \ - https://api.github.com/repos/buildkite/docs/issues/$1/comments + https://api.github.com/repos/${GH_REPO}/issues/$1/comments } function create_pull_request() { @@ -41,5 +47,5 @@ function create_pull_request() { -H "Authorization: Bearer $GH_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ --data "{\"title\":\"$1\", \"body\":\"$2\", \"head\":\"$BRANCH\", \"base\":\"main\"}" \ - "https://api.github.com/repos/buildkite/docs/pulls" + "https://api.github.com/repos/${GH_REPO}/pulls" } From 35390034d18f3272f778ac50cc52cabd91751ced Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Fri, 13 Sep 2024 16:38:16 +1000 Subject: [PATCH 127/369] add more verbose logging --- bin/prepare-preview | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bin/prepare-preview b/bin/prepare-preview index 6219f7624a..0a9139113b 100755 --- a/bin/prepare-preview +++ b/bin/prepare-preview @@ -4,6 +4,10 @@ set -eu . bin/utils.sh +echo "Using github repo: $GH_REPO" + +echo "--- :github: Fetching pull request number for branch $BUILDKITE_BRANCH" + PULL_REQUEST_NUMBER=$(get_branch_pull_request_number $BUILDKITE_BRANCH) if [ -z "$PULL_REQUEST_NUMBER" ]; then @@ -11,6 +15,8 @@ if [ -z "$PULL_REQUEST_NUMBER" ]; then exit 1 fi +echo "--- :netlify: Creating placeholder site for pull request $PULL_REQUEST_NUMBER" + # Create placeholder site whilst we build the real thing. mkdir build cat << EOF > build/index.html From 04587fa34ec1e0abea415c1cc97be724e9cd3e7f Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Mon, 16 Sep 2024 12:41:19 +1000 Subject: [PATCH 128/369] use more obscure urls for private previews --- .buildkite/pipeline.preview.yml | 1 + bin/deploy-preview | 5 ++++- bin/prepare-preview | 6 ++++-- bin/utils.sh | 7 +++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.buildkite/pipeline.preview.yml b/.buildkite/pipeline.preview.yml index ababc6c7e7..71add37abe 100644 --- a/.buildkite/pipeline.preview.yml +++ b/.buildkite/pipeline.preview.yml @@ -18,4 +18,5 @@ steps: - GH_TOKEN - NETLIFY_AUTH_TOKEN - NETLIFY_SITE_ID + - NETLIFY_SALT - BUILDKITE_BRANCH diff --git a/bin/deploy-preview b/bin/deploy-preview index cb07a739ae..34d9be6041 100755 --- a/bin/deploy-preview +++ b/bin/deploy-preview @@ -20,8 +20,11 @@ DD_RUM_ENABLED="true" DD_RUM_ENV="preview" DD_RUM_VERSION=${PULL_REQUEST_NUMBER} # Copy images into build directory mkdir -p build/docs/assets && cp -r public/docs/assets build/docs +# Generate preview id +PREVIEW_BRANCH_ID=$(netlify_preview_id "$BUILDKITE_BRANCH" "$NETLIFY_SALT") + echo "+++ :netlify: Deploying to Netlify" -node_modules/.bin/netlify deploy --auth $NETLIFY_AUTH_TOKEN --site $NETLIFY_SITE_ID --alias $PULL_REQUEST_NUMBER --dir build +node_modules/.bin/netlify deploy --auth $NETLIFY_AUTH_TOKEN --site $NETLIFY_SITE_ID --alias $PREVIEW_BRANCH_ID --dir build echo "--- :buildkite: Update annotation state" buildkite-agent annotate --style "success" --context "netlify/preview-url" diff --git a/bin/prepare-preview b/bin/prepare-preview index 0a9139113b..47723b9fa8 100755 --- a/bin/prepare-preview +++ b/bin/prepare-preview @@ -34,12 +34,14 @@ EOF zip -r build.zip build +# Generate preview id +PREVIEW_BRANCH_ID=$(netlify_preview_id "$BUILDKITE_BRANCH" "$NETLIFY_SALT") + echo "--- :netlify: Deploying placeholder site Netlify" curl -H "Content-Type: application/zip" \ -H "Authorization: Bearer $NETLIFY_AUTH_TOKEN" \ --data-binary "@build.zip" \ - https://api.netlify.com/api/v1/sites/$NETLIFY_SITE_ID/deploys\?deploy-preview\=true\&branch\=$PULL_REQUEST_NUMBER > netlify-deploy.json - + https://api.netlify.com/api/v1/sites/$NETLIFY_SITE_ID/deploys\?deploy-preview\=true\&branch\=$PREVIEW_BRANCH_ID > netlify-deploy.json PREVIEW_URL=$(cat netlify-deploy.json | jq -r '.deploy_ssl_url') PREVIEW_MESSAGE="Preview URL: $PREVIEW_URL" diff --git a/bin/utils.sh b/bin/utils.sh index 371957d47f..8584e37e4b 100644 --- a/bin/utils.sh +++ b/bin/utils.sh @@ -49,3 +49,10 @@ function create_pull_request() { --data "{\"title\":\"$1\", \"body\":\"$2\", \"head\":\"$BRANCH\", \"base\":\"main\"}" \ "https://api.github.com/repos/${GH_REPO}/pulls" } + +function netlify_preview_id() { + local branch=$1 + local salt=$2 + + echo -n "${salt}${branch}" | sha1sum | awk '{print $1}' +} From 91f7b62ec8f9496a58d6676f5efae728060f5659 Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Mon, 16 Sep 2024 12:47:03 +1000 Subject: [PATCH 129/369] use plain buildkit progress for cleaner bk logs --- .buildkite/pipeline.deploy.yml | 3 +++ .buildkite/pipeline.preview.yml | 3 +++ .buildkite/pipeline.yml | 3 +++ 3 files changed, 9 insertions(+) diff --git a/.buildkite/pipeline.deploy.yml b/.buildkite/pipeline.deploy.yml index 04fd86238a..aeb05f28c6 100644 --- a/.buildkite/pipeline.deploy.yml +++ b/.buildkite/pipeline.deploy.yml @@ -1,3 +1,6 @@ +env: + BUILDKIT_PROGRESS: plain + steps: - name: ":docker::ecr: Push to ECR" command: ".buildkite/push-image.sh" diff --git a/.buildkite/pipeline.preview.yml b/.buildkite/pipeline.preview.yml index 71add37abe..5928773b98 100644 --- a/.buildkite/pipeline.preview.yml +++ b/.buildkite/pipeline.preview.yml @@ -1,3 +1,6 @@ +env: + BUILDKIT_PROGRESS: plain + steps: - label: "Prepare preview" command: bin/prepare-preview diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 192e188bea..650d2a310f 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -1,3 +1,6 @@ +env: + BUILDKIT_PROGRESS: plain + steps: - label: ":spider: muffet" # We need to wait until rails has started before running muffet as otherwise it will error out From 5553a046b16d9e309a5584697b215d7e656f1ad9 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 16 Sep 2024 13:27:27 +1000 Subject: [PATCH 130/369] Remaining minor wording tweaks. --- pages/test_engine/dotnet_collectors.md | 2 +- pages/test_engine/elixir_collectors.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/test_engine/dotnet_collectors.md b/pages/test_engine/dotnet_collectors.md index 1e0a0f548e..9d36baa63c 100644 --- a/pages/test_engine/dotnet_collectors.md +++ b/pages/test_engine/dotnet_collectors.md @@ -32,4 +32,4 @@ Before you start, make sure .NET runs with access to [CI environment variables]( 1. Verify that it works -If all is well, you should see the test run in the analytics section of the Buildkite dashboard. +If all is well, you should see the test run analytics on the Buildkite Test Engine dashboard. diff --git a/pages/test_engine/elixir_collectors.md b/pages/test_engine/elixir_collectors.md index aea3fd308d..594b7943ff 100644 --- a/pages/test_engine/elixir_collectors.md +++ b/pages/test_engine/elixir_collectors.md @@ -60,4 +60,4 @@ Before you start, make sure ExUnit runs with access to [CI environment variables Randomized with seed 12345 ``` -1. Verify that it works. If all is well, you should see the test run in the analytics section of the Buildkite dashboard. +1. Verify that it works. If all is well, you should see the test run analytics on the Buildkite Test Engine dashboard. From db65a82beee012a743369ade5b34d97c965de80c Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 16 Sep 2024 13:57:51 +1000 Subject: [PATCH 131/369] Complete nav merge --- data/nav.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/nav.yml b/data/nav.yml index 5dd73ebafa..045cbcbb56 100644 --- a/data/nav.yml +++ b/data/nav.yml @@ -413,8 +413,8 @@ path: "integrations/pagerduty" - name: "Slack" path: "integrations/slack" -- name: "Test Analytics" - path: "test-analytics" +- name: "Test Engine" + path: "test-engine" children: - name: "Overview" path: "test-engine" From 5eaa1abc22849d3a3d6a3a351831dcd78eb1b19a Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 16 Sep 2024 14:00:41 +1000 Subject: [PATCH 132/369] Fix Test Engine landing page. --- pages/test_analytics.md | 53 ----------------------------------------- pages/test_engine.md | 29 +++++++++++----------- 2 files changed, 15 insertions(+), 67 deletions(-) delete mode 100644 pages/test_analytics.md diff --git a/pages/test_analytics.md b/pages/test_analytics.md deleted file mode 100644 index 05ecc3712f..0000000000 --- a/pages/test_analytics.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -template: "landing_page" ---- - -# Buildkite Test Engine - -Scale out your testing across any framework with Buildkite Test Engine. Get more out of fewer tests with performance insights to speed up builds and isolate unreliable tests. - -Where [Buildkite Pipelines](/docs/pipelines) helps you automate your CI/CD pipelines, Test Engine helps you track and analyze the steps in these pipelines, by: - -- Shipping code to production faster through test suite optimization. -- Working directly with Buildkite Pipelines, as well as other CI/CD applications. -- Identifying, fixing, and monitoring test suite performance. -- Tracking, improving, and monitoring test suite reliability. - -<%= image "overview.png", width: 975, height: 205, alt: "Screenshot of test suite trend showing five metrics over 28 days" %> - -## Get started - -Run through the 'Getting started' section of these Test Engine docs, beginning with [Configuring test suites](/docs/test-analytics/test-suites) for an overview of Test Engine's concepts and functionality, followed by the appropriate test collector for project's language: - - - -
- <%= button ":rspec: RSpec", "/docs/test-analytics/ruby-collectors#rspec-collector" %> - <%= button ":ruby: minitest", "/docs/test-analytics/ruby-collectors#minitest-collector" %> - <%= button ":jest: Jest", "/docs/test-analytics/javascript-collectors#configure-the-test-framework-jest" %> - <%= button ":mocha: Mocha", "/docs/test-analytics/javascript-collectors#configure-the-test-framework-mocha" %> - <%= button ":cypress: Cypress", "/docs/test-analytics/javascript-collectors#configure-the-test-framework-cypress" %> - <%= button ":jasmine: Jasmine", "/docs/test-analytics/javascript-collectors#configure-the-test-framework-jasmine" %> - <%= button ":playwright: Playwright", "/docs/test-analytics/javascript-collectors#configure-the-test-framework-playwright" %> - <%= button ":swift: Swift", "/docs/test-analytics/swift-collectors" %> - <%= button ":android: Android", "/docs/test-analytics/android-collectors" %> - <%= button ":pytest: pytest", "/docs/test-analytics/python-collectors" %> - <%= button ":golang: Go", "/docs/test-analytics/golang-collectors" %> - <%= button ":junit: JUnit", "/docs/test-analytics/importing-junit-xml" %> - <%= button ":dotnet: .NET", "/docs/test-analytics/dotnet-collectors" %> - <%= button ":elixir: Elixir", "/docs/test-analytics/elixir-collectors" %> - <%= button ":rust: Rust", "/docs/test-analytics/rust-collectors" %> -
- - - -You can also upload test results by importing [JSON](/docs/test-analytics/importing-json) or [JUnit XML](/docs/test-analytics/importing-junit-xml). - -
- -<%= tiles "test_analytics_features" %> - ->📘 Data retention -> The data uploaded to Test Analytics is stored in S3 and deleted after six months. - -<%= tiles "test_analytics_guides" %> diff --git a/pages/test_engine.md b/pages/test_engine.md index 0c87b04f47..fd34867c34 100644 --- a/pages/test_engine.md +++ b/pages/test_engine.md @@ -2,20 +2,23 @@ template: "landing_page" --- -# Buildkite Test Analytics +# Buildkite Test Engine -Where Buildkite Pipelines help you automate your build pipelines, -Test Analytics helps you track and analyze the steps in that pipeline that involve tests: +Scale out your testing across any framework with Buildkite Test Engine. Get more out of fewer tests with performance insights to speed up builds and isolate unreliable tests -- Ship code to production faster by optimizing test suites -- Works with any continuous integration -- Identify, fix, and monitor test suite performance -- Track, improve, and monitor test suite reliability +Where [Buildkite Pipelines](/docs/pipelines) helps you automate your CI/CD pipelines, Test Engine helps you track and analyze the steps in these pipelines, by: + +- Shipping code to production faster through test suite optimization. +- Working directly with Buildkite Pipelines, as well as other CI/CD applications. +- Identifying, fixing, and monitoring test suite performance. +- Tracking, improving, and monitoring test suite reliability. <%= image "overview.png", width: 975, height: 205, alt: "Screenshot of test suite trend showing five metrics over 28 days" %> ## Get started +Run through the 'Getting started' section of these Test Engine docs, beginning with [Configuring test suites](/docs/test-engine/test-suites) for an overview of Test Engine's concepts and functionality, followed by the appropriate test collector for project's language: +
@@ -40,13 +43,11 @@ Test Analytics helps you track and analyze the steps in that pipeline that invol You can also upload test results by importing [JSON](/docs/test-engine/importing-json) or [JUnit XML](/docs/test-engine/importing-junit-xml). ->📘 Data retention -> The data uploaded to Test Analytics is stored in S3 and deleted after six months. - ----- +
-<%= tiles "test_analytics_features" %> +<%= tiles "test_engine_features" %> ----- +> 📘 Data retention +> The data uploaded to Test Engine is stored in S3 and deleted after six months. -<%= tiles "test_analytics_guides" %> +<%= tiles "test_engine_guides" %> From b20b38bcb12f7ca71f9bcbd3ac9612e5d9b5ca5a Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 16 Sep 2024 14:11:56 +1000 Subject: [PATCH 133/369] Resolve remaining broken links due to page rearrangements. --- pages/packages/security/permissions.md | 2 +- pages/platform.md | 2 +- pages/team_management/permissions.md | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pages/packages/security/permissions.md b/pages/packages/security/permissions.md index bf1fad1ce9..02dde7a9be 100644 --- a/pages/packages/security/permissions.md +++ b/pages/packages/security/permissions.md @@ -22,7 +22,7 @@ As an organization administrator, you can access the [**Organization Settings** - Add new teams or edit existing ones in the [**Team** section](https://buildkite.com/organizations/~/teams). - * After selecting a team, you can view and administer the member-, [pipeline-](/docs/team-management/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions), [registry-](#manage-teams-and-permissions-registry-level-permissions) and [team-](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions)level settings for that team. + * After selecting a team, you can view and administer the member-, [pipeline-](/docs/pipelines/security/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions), [registry-](#manage-teams-and-permissions-registry-level-permissions) and [team-](/docs/team-management/permissions#manage-teams-and-permissions-team-level-permissions)level settings for that team. - [Enable Buildkite Packages](#enabling-buildkite-packages) for your Buildkite organization. diff --git a/pages/platform.md b/pages/platform.md index b1ffffcae1..b8e712948e 100644 --- a/pages/platform.md +++ b/pages/platform.md @@ -6,7 +6,7 @@ template: "landing_page" The Buildkite Scale-Out Delivery Platform is an adaptable, composable, and scalable platform with everything platform teams need to build software delivery systems for their businesses—and rapidly deliver value to users. -The Buildkite platform documentation contains docs for _platform_-level features of Buildkite available across Buildkite [Pipelines](/docs/pipelines), [Test Engine](/docs/test-analytics), as well as [Packages](/docs/packages). This area of the docs covers the following topics: +The Buildkite platform documentation contains docs for _platform_-level features of Buildkite available across Buildkite [Pipelines](/docs/pipelines), [Test Engine](/docs/test-engine), as well as [Packages](/docs/packages). This area of the docs covers the following topics: - [Team management](/docs/team-management), with guidelines on how to manage your users and teams across the Buildkite platform for Pipelines, Test Engine and Packages. diff --git a/pages/team_management/permissions.md b/pages/team_management/permissions.md index 4ea70272ff..16cc0fc588 100644 --- a/pages/team_management/permissions.md +++ b/pages/team_management/permissions.md @@ -31,7 +31,7 @@ A user who is a _Buildkite organization administrator_ can access the [**Organiz - From the **Teams** page: * Create a new team, using the **New Team** button. - * Administer (with full control) the [team-](#manage-teams-and-permissions-team-level-permissions), [pipeline-](#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions) and [registry-](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions)level settings throughout their Buildkite organization. + * Administer (with full control) the [team-](#manage-teams-and-permissions-team-level-permissions), [pipeline-](/docs/pipelines/security/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suite-](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions) and [registry-](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions)level settings throughout their Buildkite organization. **Note:** Registry-level settings are only available once [Buildkite Packages has been enabled](/docs/packages/permissions#enabling-buildkite-packages). @@ -57,7 +57,7 @@ A user who is a _team maintainer_ on an existing team can: * Remove a user from this team, by selecting the user's **Remove** button. * Change the permission for all users in this team on any: - - [pipeline](#manage-teams-and-permissions-pipeline-level-permissions) in the team to **Full Access**, **Build & Read** or **Read Only**. + - [pipeline](/docs/pipelines/security/permissions#manage-teams-and-permissions-pipeline-level-permissions) in the team to **Full Access**, **Build & Read** or **Read Only**. - [test suite](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions) in the team to **Full Access** or **Read Only**. - [registry](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) in the team to **Full Access**, **Read & Write** or **Read Only**. @@ -84,7 +84,7 @@ A user who is a _team maintainer_ on an existing team can: As indicated in the Buildkite interface, a user who is in a team is known as a **Team Member**, and such users have fewer permissions within the team (that is, no team management capabilities) than a **Team Maintainer**. -All team members in a team have the same level of access to the [pipelines](#manage-teams-and-permissions-pipeline-level-permissions), [test suites](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions), and [registries](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) in the team. If you need to have more fine grained control over the pipelines, test suites or registries in a team, you can create more teams with different permissions. +All team members in a team have the same level of access to the [pipelines](/docs/pipelines/security/permissions#manage-teams-and-permissions-pipeline-level-permissions), [test suites](/docs/test-engine/permissions#manage-teams-and-permissions-test-suite-level-permissions), and [registries](/docs/packages/permissions#manage-teams-and-permissions-registry-level-permissions) in the team. If you need to have more fine grained control over the pipelines, test suites or registries in a team, you can create more teams with different permissions. > 🚧 Changing **Full Access** permissions on pipelines, test suites and registries > As a team maintainer, once you change the permission on any of these items away from **Full Access**, you could lose the ability to change the permissions on that item again. This can happen if you are no longer a member of a team that provides **Full Access** to this item. From d5023087a2650d9cfff2612f7b29c056f19c6f06 Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Mon, 16 Sep 2024 14:19:56 +1000 Subject: [PATCH 134/369] skeleton for new sync-public-pr-pipeline --- .buildkite/pipeline.sync_public_pr.yml | 9 +++++++++ bin/sync-public-pr | 5 +++++ 2 files changed, 14 insertions(+) create mode 100644 .buildkite/pipeline.sync_public_pr.yml create mode 100755 bin/sync-public-pr diff --git a/.buildkite/pipeline.sync_public_pr.yml b/.buildkite/pipeline.sync_public_pr.yml new file mode 100644 index 0000000000..8633161cab --- /dev/null +++ b/.buildkite/pipeline.sync_public_pr.yml @@ -0,0 +1,9 @@ +steps: + - input: "Which PR needs to be synced?" + fields: + - key: pull_request_number + required: true + text: "Pull Request Number" + format: "^[0-9]+$" + + - command: "bin/sync-public-pr" diff --git a/bin/sync-public-pr b/bin/sync-public-pr new file mode 100755 index 0000000000..779b098295 --- /dev/null +++ b/bin/sync-public-pr @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +PR_NUMBER=$(buildkite-agent meta-data get "pull_request_number") + +echo "PR_NUMBER: $PR_NUMBER" From fdff3ed29548468afd419bacbcd14bceb1b45ee3 Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Mon, 16 Sep 2024 14:24:09 +1000 Subject: [PATCH 135/369] explicit depends_on --- .buildkite/pipeline.sync_public_pr.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.buildkite/pipeline.sync_public_pr.yml b/.buildkite/pipeline.sync_public_pr.yml index 8633161cab..ced769d08c 100644 --- a/.buildkite/pipeline.sync_public_pr.yml +++ b/.buildkite/pipeline.sync_public_pr.yml @@ -1,5 +1,6 @@ steps: - input: "Which PR needs to be synced?" + key: collect_pr_number fields: - key: pull_request_number required: true @@ -7,3 +8,4 @@ steps: format: "^[0-9]+$" - command: "bin/sync-public-pr" + depends_on: collect_pr_number From 832fe5bae055782bf2d351c1458edfef4bc69350 Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Mon, 16 Sep 2024 14:36:05 +1000 Subject: [PATCH 136/369] sync pr refs to private repo --- bin/sync-public-pr | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/bin/sync-public-pr b/bin/sync-public-pr index 779b098295..440f1bb306 100755 --- a/bin/sync-public-pr +++ b/bin/sync-public-pr @@ -1,5 +1,15 @@ #!/usr/bin/env bash +set -euo pipefail + PR_NUMBER=$(buildkite-agent meta-data get "pull_request_number") +TARGET_BRANCH="docs-public-pr-${PR_NUMBER}" +SOURCE_REF="pull/${PR_NUMBER}/head" + +echo "+++ Syncing PR #${PR_NUMBER} to local branch ${TARGET_BRANCH}" + +set -x + +git fetch "git@github.com:${PUBLIC_GH_REPO}.git" "${SOURCE_REF}:${TARGET_BRANCH}" -echo "PR_NUMBER: $PR_NUMBER" +git push --force origin "${TARGET_BRANCH}" From 6db071111eede68b964c6d32e72c427cf71c948c Mon Sep 17 00:00:00 2001 From: Chris Atkins Date: Mon, 16 Sep 2024 14:44:29 +1000 Subject: [PATCH 137/369] annotate build with link to open PR --- bin/sync-public-pr | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bin/sync-public-pr b/bin/sync-public-pr index 440f1bb306..ba23d89e45 100755 --- a/bin/sync-public-pr +++ b/bin/sync-public-pr @@ -13,3 +13,12 @@ set -x git fetch "git@github.com:${PUBLIC_GH_REPO}.git" "${SOURCE_REF}:${TARGET_BRANCH}" git push --force origin "${TARGET_BRANCH}" + +CREATE_PR_LINK="https://github.com/${PRIVATE_GH_REPO}/compare/${TARGET_BRANCH}?expand=1" + +ANNOTATION_CONTENT="Synced PR ${PUBLIC_GH_REPO}#${PR_NUMBER} to private repo branch \`${TARGET_BRANCH}\`. + +Open PR: ${CREATE_PR_LINK} +" + +buildkite-agent annotate --context pr-link --style "info" "${ANNOTATION_CONTENT}" From d4e347dbd82ee8acd0d7d7085ade5ae2996fe94e Mon Sep 17 00:00:00 2001 From: Samuel Cochran Date: Mon, 16 Sep 2024 12:39:50 +0700 Subject: [PATCH 138/369] Add optional cluster and queue oidc claims --- pages/agent/v3/cli_oidc.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pages/agent/v3/cli_oidc.md b/pages/agent/v3/cli_oidc.md index 33037405c4..2fac890026 100644 --- a/pages/agent/v3/cli_oidc.md +++ b/pages/agent/v3/cli_oidc.md @@ -185,6 +185,34 @@ Generate these additional claims by adding `--claims` to the `buildkite-agent oi

Example: 0184990a-4782-42b5-afc1-16715b10b1l0

+ + cluster_id + +

The cluster UUID if using clusters.

+

Example: 0191f956-042f-7ec4-aa62-8e5eeae396d0

+ + + + cluster_name + +

The cluster name if using clusters.

+

Example: default

+ + + + queue_id + +

The cluster queue UUID if using clusters.

+

Example: 0191f956-62da-7515-b79b-bdecb519aa32

+ + + + queue_key + +

The cluster queue key if using clusters.

+

Example: runners

+ + From 18fd9fbe500ee790d4b85ee0bdacbfe711094852 Mon Sep 17 00:00:00 2001 From: Giles Gaskell Date: Mon, 16 Sep 2024 16:43:23 +1000 Subject: [PATCH 139/369] Add Packages to Buildkite Docs landing page. --- app/views/homepage/_products.html.erb | 11 ++++- app/views/homepage/_references.html.erb | 55 ++++++++++++++----------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/app/views/homepage/_products.html.erb b/app/views/homepage/_products.html.erb index 45f5a7854b..d6b9ebc63a 100644 --- a/app/views/homepage/_products.html.erb +++ b/app/views/homepage/_products.html.erb @@ -14,7 +14,16 @@ Test Engine graphic

Test Engine

-

Real-time tracking and monitoring for your tests

+

Real-time, scalable tracking and monitoring for your tests

+
+ + + +
+ Packages graphic +
+

Packages

+

Scale out asset management across any ecosystem

diff --git a/app/views/homepage/_references.html.erb b/app/views/homepage/_references.html.erb index 20a0a1406f..b1b867764a 100644 --- a/app/views/homepage/_references.html.erb +++ b/app/views/homepage/_references.html.erb @@ -1,7 +1,7 @@ <% references = [ { - title: "Pipeline", + title: "Pipelines", links: [ { title: "Step types", @@ -23,41 +23,29 @@ ] }, { - title: "Agent", + title: "Test Engine", links: [ { - title: "Configuration settings", - link: "/docs/agent/v3/configuration" - }, - { - title: "Lifecycle hooks", - link: "/docs/agent/v3/hooks" - }, - { - title: "CLI commands", - link: "/docs/agent/v3/cli-reference" + title: "JSON test results", + link: "/docs/test-engine/importing-json" }, { - title: "Elastic Stack template parameters", - link: "/docs/agent/v3/elastic-ci-aws/parameters" + title: "Environment variables", + link: "/docs/test-engine/ci-environments" } ] }, { - title: "Test Engine", + title: "Packages", links: [ { - title: "JSON test results", - link: "/docs/test-engine/importing-json" - }, - { - title: "Environment variables", - link: "/docs/test-engine/ci-environments" + title: "Package ecosystems", + link: "/docs/packages/alpine" } ] }, { - title: "API", + title: "APIs", links: [ { title: "REST API", @@ -72,13 +60,34 @@ link: "/docs/apis/webhooks" } ] + }, + { + title: "Agent", + links: [ + { + title: "Configuration settings", + link: "/docs/agent/v3/configuration" + }, + { + title: "Lifecycle hooks", + link: "/docs/agent/v3/hooks" + }, + { + title: "CLI commands", + link: "/docs/agent/v3/cli-reference" + }, + { + title: "Elastic Stack template parameters", + link: "/docs/agent/v3/elastic-ci-aws/parameters" + } + ] } ] %>
-

APIs and references

+

References and APIs