Skip to content

Commit

Permalink
Merge pull request #1497 from argos-ci/build-filtering
Browse files Browse the repository at this point in the history
feat: builds filtering
  • Loading branch information
gregberge authored Jan 2, 2025
2 parents 217da9d + 1941be0 commit 982b2a6
Show file tree
Hide file tree
Showing 21 changed files with 661 additions and 299 deletions.
4 changes: 3 additions & 1 deletion apps/backend/src/database/models/Build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { ScreenshotBucket } from "./ScreenshotBucket.js";
import { ScreenshotDiff } from "./ScreenshotDiff.js";
import { User } from "./User.js";

export const BUILD_EXPIRATION_DELAY_MS = 2 * 3600 * 1000; // 2 hours

export type BuildType = "reference" | "check" | "orphan";

const BuildStatusSchema = z.enum([
Expand Down Expand Up @@ -327,7 +329,7 @@ export class Build extends Model {
case "pending":
case "progress":
return Date.now() - new Date(build.createdAt).getTime() >
2 * 3600 * 1000
BUILD_EXPIRATION_DELAY_MS
? ("expired" as const)
: ("pending" as const);

Expand Down
11 changes: 9 additions & 2 deletions apps/backend/src/graphql/__generated__/resolver-types.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions apps/backend/src/graphql/__generated__/schema.gql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

121 changes: 111 additions & 10 deletions apps/backend/src/graphql/definitions/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import type { PartialModelObject } from "objection";
import {
Account,
Build,
BUILD_EXPIRATION_DELAY_MS,
GitlabProject,
Project,
ProjectUser,
Screenshot,
ScreenshotDiff,
User,
} from "@/database/models/index.js";
import {
Expand All @@ -23,6 +25,7 @@ import { formatGlProject, getGitlabClientFromAccount } from "@/gitlab/index.js";
import { getOrCreateGithubRepository } from "@/graphql/services/github.js";

import {
IBuildStatus,
IProjectPermission,
IProjectUserLevel,
IResolvers,
Expand Down Expand Up @@ -76,12 +79,22 @@ export const typeDefs = gql`
view
}
input BuildsFilterInput {
name: String
type: [BuildType!]
status: [BuildStatus!]
}
type Project implements Node {
id: ID!
name: String!
token: String
"Builds associated to the repository"
builds(first: Int = 30, after: Int = 0, buildName: String): BuildConnection!
builds(
first: Int = 30
after: Int = 0
filters: BuildsFilterInput
): BuildConnection!
"A single build linked to the repository"
build(number: Int!): Build
"Latest auto-approved build"
Expand Down Expand Up @@ -126,11 +139,7 @@ export const typeDefs = gql`
extend type Query {
"Get a project"
project(
accountSlug: String!
projectName: String!
buildName: String
): Project
project(accountSlug: String!, projectName: String!): Project
"Get a project"
projectById(id: ID!): Project
}
Expand Down Expand Up @@ -448,12 +457,104 @@ export const resolvers: IResolvers = {
latestBuild: async (project, _args, ctx) => {
return ctx.loaders.LatestProjectBuild.load(project.id);
},
builds: async (project, { first, after, buildName }) => {
builds: async (project, { first, after, filters }) => {
const result = await Build.query()
.where({ projectId: project.id })
.where((query) =>
buildName ? query.where({ name: buildName }) : query,
)
.where((query) => {
if (filters?.name) {
query.where("name", filters.name);
}
if (filters?.type) {
query.whereIn("type", filters.type);
}
const status = filters?.status;
if (status) {
query.where((qb) => {
// Job status check
if (!status.includes(IBuildStatus.Aborted)) {
qb.whereNot("jobStatus", "aborted");
}

if (!status.includes(IBuildStatus.Error)) {
qb.whereNot("jobStatus", "error");
}

if (!status.includes(IBuildStatus.Expired)) {
qb.whereNot((qb) => {
qb.whereIn("jobStatus", ["progress", "pending"]).whereRaw(
`now() - "builds"."createdAt" > interval '${BUILD_EXPIRATION_DELAY_MS} milliseconds'`,
);
});
}

if (!status.includes(IBuildStatus.Progress)) {
qb.whereNot((qb) => {
qb.where((qb) =>
// Job is in progress
// or job is complete without a conclusion, we assume it's in progress
qb
.where("jobStatus", "progress")
.orWhere((qb) =>
qb
.where("jobStatus", "complete")
.whereNull("conclusion"),
),
).whereRaw(
`now() - "builds"."createdAt" < interval '${BUILD_EXPIRATION_DELAY_MS} milliseconds'`,
);
});
}

if (!status.includes(IBuildStatus.Pending)) {
qb.whereNot((qb) => {
qb.where("jobStatus", "pending").whereRaw(
`now() - "builds"."createdAt" < interval '${BUILD_EXPIRATION_DELAY_MS} milliseconds'`,
);
});
}

if (!status.includes(IBuildStatus.Accepted)) {
qb.whereNotExists(
ScreenshotDiff.query()
.whereRaw('screenshot_diffs."buildId" = builds.id')
.where("validationStatus", "accepted"),
);
}

if (!status.includes(IBuildStatus.Rejected)) {
qb.whereNotExists(
ScreenshotDiff.query()
.whereRaw('screenshot_diffs."buildId" = builds.id')
.where("validationStatus", "rejected"),
);
}

if (!status.includes(IBuildStatus.ChangesDetected)) {
qb.where((qb) => {
qb.whereNot("conclusion", "changes-detected")
.orWhereNull("conclusion")
.orWhereExists(
ScreenshotDiff.query()
.whereRaw('screenshot_diffs."buildId" = builds.id')
.whereIn("validationStatus", ["rejected", "accepted"]),
);
});
}

if (!status.includes(IBuildStatus.NoChanges)) {
qb.where((qb) => {
qb.whereNot("conclusion", "no-changes")
.orWhereNull("conclusion")
.orWhereExists(
ScreenshotDiff.query()
.whereRaw('screenshot_diffs."buildId" = builds.id')
.whereIn("validationStatus", ["rejected", "accepted"]),
);
});
}
});
}
})
.orderBy([
{ column: "createdAt", order: "desc" },
{ column: "number", order: "desc" },
Expand Down
Loading

0 comments on commit 982b2a6

Please sign in to comment.