Skip to content

Commit

Permalink
fix: better CI handling, specially Buildkite
Browse files Browse the repository at this point in the history
  • Loading branch information
gregberge committed Jan 9, 2023
1 parent 0ae6b18 commit b6bc04c
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 65 deletions.
25 changes: 25 additions & 0 deletions packages/core/src/ci-environment/env-ci.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import envCi from "env-ci";
import type { CiEnvironment, Context } from "./types";

export const getCiEnvironmentFromEnvCi = (
ctx: Context
): CiEnvironment | null => {
const ciContext = envCi(ctx);
const name = ciContext.isCi
? ciContext.name ?? null
: ciContext.commit
? "Git"
: null;
const commit = ciContext.commit ?? null;
const branch = (ciContext.branch || ciContext.prBranch) ?? null;
const slug = ciContext.slug ? ciContext.slug.split("/") : null;
const owner = slug ? slug[0] : null;
const repository = slug ? slug[1] : null;
const jobId = ciContext.job ?? null;
const runId = null;
const prNumber = null;

return commit
? { name, commit, branch, owner, repository, jobId, runId, prNumber }
: null;
};
16 changes: 16 additions & 0 deletions packages/core/src/ci-environment/git.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { head, branch } from "./git";

describe("#head", () => {
it("returns the current commit", () => {
expect(head()).toMatch(/^[0-9a-f]{40}$/);
});
});

/**
* This test can be run locally, too hard to make it work on CI.
*/
xdescribe("#branch", () => {
it("returns the current branch", () => {
expect(branch()).toBe("main");
});
});
31 changes: 31 additions & 0 deletions packages/core/src/ci-environment/git.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { execSync } from "node:child_process";

/**
* Returns the head commit.
*/
export const head = () => {
try {
return execSync("git rev-parse HEAD").toString().trim();
} catch {
return null;
}
};

/**
* Returns the current branch.
*/
export const branch = () => {
try {
const headRef = execSync("git rev-parse --abbrev-ref HEAD")
.toString()
.trim();

if (headRef === "HEAD") {
return null;
}

return headRef;
} catch {
return null;
}
};
30 changes: 5 additions & 25 deletions packages/core/src/ci-environment/index.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,24 @@
import envCi from "env-ci";
import buildkite from "./services/buildkite";
import heroku from "./services/heroku";
import githubActions from "./services/github-actions";
import circleci from "./services/circleci";
import travis from "./services/travis";
import type { CiEnvironment, Options, Context } from "./types";
import type { CiEnvironment, Options } from "./types";
import { debug } from "../debug";
import { getCiEnvironmentFromEnvCi } from "./env-ci";

export { CiEnvironment };

const services = [heroku, githubActions, circleci, travis, buildkite];

export const envCiDetection = (ctx: Context) => {
const ciContext = envCi(ctx);
const name = ciContext.isCi
? ciContext.name ?? null
: ciContext.commit
? "Git"
: null;
const commit = ciContext.commit ?? null;
const branch = (ciContext.branch || ciContext.prBranch) ?? null;
const slug = ciContext.slug ? ciContext.slug.split("/") : null;
const owner = slug ? slug[0] : null;
const repository = slug ? slug[1] : null;
const jobId = ciContext.job ?? null;
const runId = null;
const prNumber = null;

return commit
? { name, commit, branch, owner, repository, jobId, runId, prNumber }
: null;
};

export const getCiEnvironment = ({
env = process.env,
}: Options = {}): CiEnvironment | null => {
const ctx = { env };
debug("Detecting CI environment", { env });
const service = services.find((service) => service.detect(ctx));

// Internal service matched
// Service matched
if (service) {
debug("Internal service matched", service.name);
const variables = service.config(ctx);
Expand All @@ -48,8 +27,9 @@ export const getCiEnvironment = ({
return ciEnvironment;
}

// We fallback on "env-ci" library, not very good but it's better than nothing
debug("Falling back on env-ci");
const ciEnvironment = envCiDetection(ctx);
const ciEnvironment = getCiEnvironmentFromEnvCi(ctx);
debug("CI environment", ciEnvironment);
return ciEnvironment;
};
13 changes: 6 additions & 7 deletions packages/core/src/ci-environment/services/buildkite.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import type { Service } from "../types";
import { envCiDetection } from "../index";
import { head, branch } from "../git";

const service: Service = {
name: "Buildkite",
detect: ({ env }) => Boolean(env.BUILDKITE),
config: ({ env }) => {
const ciProps = envCiDetection({ env });

return {
commit: ciProps?.commit || null,
branch: env.BUILDKITE_BRANCH || null,
// Buildkite doesn't work well so we fallback to git to ensure we have commit and branch
commit: env.BUILDKITE_COMMIT || head() || null,
branch: env.BUILDKITE_BRANCH || branch() || null,
owner: env.BUILDKITE_ORGANIZATION_SLUG || null,
repository: env.BUILDKITE_PROJECT_SLUG || null,
jobId: env.BUILDKITE_JOB_ID || null,
runId: ciProps?.runId || null,
jobId: null,
runId: null,
prNumber: env.BUILDKITE_PULL_REQUEST
? Number(env.BUILDKITE_PULL_REQUEST)
: null,
Expand Down
21 changes: 9 additions & 12 deletions packages/core/src/ci-environment/services/circleci.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { Service, Context } from "../types";
import { envCiDetection } from "../index";

const getPrNumber = ({ env }: Context) => {
const branchRegex = /pull\/(\d+)/;
const branchMatches = branchRegex.exec(env.CIRCLE_PULL_REQUEST || "");
if (branchMatches) {
return Number(branchMatches[1]);
const matches = branchRegex.exec(env.CIRCLE_PULL_REQUEST || "");
if (matches) {
return Number(matches[1]);
}

return null;
Expand All @@ -15,15 +14,13 @@ const service: Service = {
name: "CircleCI",
detect: ({ env }) => Boolean(env.CIRCLECI),
config: ({ env }) => {
const ciProps = envCiDetection({ env });

return {
commit: ciProps?.commit || null,
branch: ciProps?.branch || null,
owner: ciProps?.owner || null,
repository: ciProps?.repository || null,
jobId: ciProps?.jobId || null,
runId: ciProps?.runId || null,
commit: env.CIRCLE_SHA1 || null,
branch: env.CIRCLE_BRANCH || null,
owner: env.CIRCLE_PROJECT_USERNAME || null,
repository: env.CIRCLE_PROJECT_REPONAME || null,
jobId: null,
runId: null,
prNumber: getPrNumber({ env }),
};
},
Expand Down
14 changes: 7 additions & 7 deletions packages/core/src/ci-environment/services/github-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,24 @@ Please run "actions/checkout" with "fetch-depth: 2". Example:
return process.env.GITHUB_SHA ?? null;
};

function getBranch({ env }: Context) {
const getBranch = ({ env }: Context) => {
if (env.GITHUB_HEAD_REF) {
return env.GITHUB_HEAD_REF;
}

const branchRegex = /refs\/heads\/(.*)/;
const branchMatches = branchRegex.exec(env.GITHUB_REF || "");
if (branchMatches) {
return branchMatches[1];
const matches = branchRegex.exec(env.GITHUB_REF || "");
if (matches) {
return matches[1];
}

return null;
}
};

function getRepository({ env }: Context) {
const getRepository = ({ env }: Context) => {
if (!env.GITHUB_REPOSITORY) return null;
return env.GITHUB_REPOSITORY.split("/")[1];
}
};

const getPrNumber = ({ env }: Context) => {
const branchRegex = /refs\/pull\/(\d+)/;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/ci-environment/services/heroku.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const service: Service = {
branch: env.HEROKU_TEST_RUN_BRANCH || null,
owner: null,
repository: null,
jobId: env.HEROKU_TEST_RUN_ID || null,
jobId: null,
runId: null,
prNumber: null,
}),
Expand Down
38 changes: 25 additions & 13 deletions packages/core/src/ci-environment/services/travis.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
import type { Service } from "../types";
import { envCiDetection } from "../index";
import type { Context, Service } from "../types";

const getOwner = ({ env }: Context) => {
if (!env.TRAVIS_REPO_SLUG) return null;
return env.TRAVIS_REPO_SLUG.split("/")[0] || null;
};

const getRepository = ({ env }: Context) => {
if (!env.TRAVIS_REPO_SLUG) return null;
return env.TRAVIS_REPO_SLUG.split("/")[1] || null;
};

const getPrNumber = ({ env }: Context) => {
if (env.TRAVIS_PULL_REQUEST) return Number(env.TRAVIS_PULL_REQUEST);
return null;
};

const service: Service = {
name: "Travis CI",
detect: ({ env }) => Boolean(env.TRAVIS),
config: ({ env }) => {
const ciProps = envCiDetection({ env });
config: (ctx) => {
const { env } = ctx;

return {
commit: ciProps?.commit || null,
branch: ciProps?.branch || null,
owner: ciProps?.owner || null,
repository: ciProps?.repository || null,
jobId: ciProps?.jobId || null,
runId: ciProps?.runId || null,
prNumber: env.TRAVIS_PULL_REQUEST
? Number(env.TRAVIS_PULL_REQUEST)
: null,
commit: env.TRAVIS_COMMIT || null,
branch: env.TRAVIS_BRANCH || null,
owner: getOwner(ctx),
repository: getRepository(ctx),
jobId: null,
runId: null,
prNumber: getPrNumber(ctx),
};
},
};
Expand Down

0 comments on commit b6bc04c

Please sign in to comment.