From 853f9b8f787827be54698e6b28cfd7648ea216d3 Mon Sep 17 00:00:00 2001 From: Jonatan Nilsson Date: Mon, 18 Nov 2024 23:57:35 +0000 Subject: [PATCH] Implement basic ci detection --- .forgejo/workflows/deploy.yml | 52 +++++++++++++++++++++++++ README.md | 21 ++++++++++- index.d.ts | 2 + index.js | 22 +++++++++++ package-lock.json | 26 +++++++++++++ package.json | 39 +++++++++++++++++++ test.mjs | 71 +++++++++++++++++++++++++++++++++++ 7 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 .forgejo/workflows/deploy.yml create mode 100644 index.d.ts create mode 100755 index.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 test.mjs diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml new file mode 100644 index 0000000..ab630fa --- /dev/null +++ b/.forgejo/workflows/deploy.yml @@ -0,0 +1,52 @@ +on: + push: + branches: + - master + + +jobs: + deploy: + runs-on: arch + steps: + - name: Check out repository code + uses: actions/checkout@v3 + - name: Install dependencies + run: time pnpm install + - name: Run Tests + run: pnpm run test --ignore-only + - name: Sanity check it detects this CI + run: | + if node index.js; then + echo "Success" + else + echo "Failed for forgejo" + exit 1 + fi + - name: Deply if new version + run: | + echo "" + echo "------------------------------------" + echo "" + CURR_VER="$(cat package.json | jq -r .name)_v$(cat package.json | jq -r .version)" + CURR_NAME="$(cat package.json | jq -r .name) v$(cat package.json | jq -r .version)" + + echo "Checking https://git.nfp.is/api/v1/repos/${{ github.repository }}/releases for name ${CURR_NAME}" + + if curl -s -X GET -H "Authorization: token ${{ secrets.deploytoken }}" https://git.nfp.is/api/v1/repos/${{ github.repository }}/releases | grep -o "\"name\":\"${CURR_NAME}\"" > /dev/null; then + echo "Skipping ${{ github.job }} since $CURR_NAME already exists"; + exit; + fi + + echo "New release ${CURR_VER} found, beginning deployment" + + echo "Creating ${CURR_VER} release on forgejo" + curl \ + -X POST \ + -H "Authorization: token ${{ secrets.deploytoken }}" \ + -H "Content-Type: application/json" \ + https://git.nfp.is/api/v1/repos/${{ github.repository }}/releases \ + -d "{\"tag_name\":\"${CURR_VER}\",\"name\":\"${CURR_NAME}\",\"body\":\"Automatic release from Appveyor from ${{ github.sha }} :\n\n${{ github.event.head_commit.message }}\"}" | jq + + echo "//registry.npmjs.org/:_authToken=${{ secrets.npmtoken }}" > ~/.npmrc + echo "Publishing new version to npm" + npm publish diff --git a/README.md b/README.md index 114def8..82ce035 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,22 @@ # inside-ci -Quick tool to check if we are inside a CI environment \ No newline at end of file +Quick tool to check if we are inside a CI environment + +# API + +`insideCi()` + +Returns true if inside CI. Otherwise returns false. + +# CLI + +`inside-ci` + +Returns code 0 if inside CI. Otherwise returns an error code of 1. + +`inside-ci || echo 'We are not inside CI, install some stuff'` +`inside-ci && echo 'We are inside CI, install some stuff'` + +Example: + +`is-ci || husky install` diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..e156733 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,2 @@ +/** Returns true if current environment is running inside CI. Otherwise returns false. */ +export function insideCi() diff --git a/index.js b/index.js new file mode 100755 index 0000000..c670aef --- /dev/null +++ b/index.js @@ -0,0 +1,22 @@ +#!/usr/bin/env node + +/** Returns true if current environment is running inside CI. Otherwise returns false. */ +function insideCi() { + // Bail out if this is specifically overwritten to false. + // Some users seem to wanna be able to do that + if (process.env.CI === 'false') return false + + // Most CI use env.CI (travis, Gitlab, etc.) + // There are some exceptions though: + // CI_APP_ID is used by Appflow: https://ionic.io/docs/appflow/package/environments#predefined-environments + // CI_NAME is used by Codeship:https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables + // BUILD_NUMBER is used by TeamCity: https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html#Predefined+Server+Build+Parameters + // RUN_ID is used by Taskcluster: https://docs.taskcluster.net/docs/reference/workers/docker-worker/environment + return Boolean(['CI','CI_APP_ID','BUILD_NUMBER','CI_NAME','RUN_ID'].some(x => process.env[x])) +} + +module.exports.insideCi = insideCi + +if (require.main === module) { + process.exit(insideCi() ? 0 : 1) +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7637ba3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,26 @@ +{ + "name": "inside-ci", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "inside-ci", + "version": "1.0.0", + "license": "WTFPL", + "devDependencies": { + "eltro": "^1.6.1" + } + }, + "node_modules/eltro": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/eltro/-/eltro-1.6.1.tgz", + "integrity": "sha512-g291jOxdUPnXBexOedGFNhswQ4127EuPOmYG6UMkPnowCUw91JRyogoEQKxtIrnhrfGR60b98C54GUzi3pJ44Q==", + "dev": true, + "license": "WTFPL", + "bin": { + "eltro": "cli.mjs" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1581dd6 --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "inside-ci", + "version": "1.0.0", + "description": "Quick tool to check if we are inside CI", + "main": "index.js", + "scripts": { + "test": "eltro -r dot test.mjs", + "test:watch": "eltro -r dot -w test test.mjs" + }, + "bin": { + "inside-ci": "./index.js" + }, + "watch": { + "test": { + "patterns": ["index.js", "test.mjs"] + } + }, + "repository": { + "type": "git", + "url": "https://git.nfp.is/TheThing/inside-ci.git" + }, + "keywords": [ + "ci", + "is-ci", + "inside-ci", + "environment" + ], + "author": "Jonatan Nilsson", + "license": "WTFPL", + "devDependencies": { + "eltro": "^1.6.1" + }, + "files": [ + "index.js", + "index.d.ts", + "README.md", + "LICENSE" + ] +} diff --git a/test.mjs b/test.mjs new file mode 100644 index 0000000..5c26af9 --- /dev/null +++ b/test.mjs @@ -0,0 +1,71 @@ +import { exec } from 'child_process' +import { Eltro as t, assert } from 'eltro' +import { insideCi } from './index.js' + +t.describe('#insideCi()', function () { + const testVariables = [ + 'CI', + 'CI_APP_ID', + 'BUILD_NUMBER', + 'CI_NAME', + 'RUN_ID', + ] + + t.beforeEach(function () { + for (let name of testVariables) { + delete process.env[name] + } + }) + + testVariables.forEach(name => { + t.test(`env.${name} should return true if set`, function () { + process.env[name] = 'asdf' + assert.ok(insideCi()) + }) + }) + + t.test('should return false by default', function() { + assert.notOk(insideCi()) + }) + + t.test('should return false if all are empty strings', function () { + for (let name of testVariables) { + process.env[name] = '' + } + assert.notOk(insideCi()) + }) + + t.test('should return false if env.CI is specifically "false"', function () { + process.env.CI = 'false' + assert.notOk(insideCi()) + }) +}) + +function runCommand(command, options) { + return new Promise(function (res) { + exec(command, options, function (err, stdout, stderr) { + res({ + err, stdout, stderr + }) + }) + }) +} + +t.describe('CLI', function() { + t.test('should return success/code 0 if CI is filled', async function () { + let result = await runCommand('node index.js', { + env: { CI: 'true' } + }) + + assert.notOk(result.err) + }) + + t.test('should return error code 1 if CI is false', async function () { + let result = await runCommand('node index.js', { + env: { CI: 'false' } + }) + + assert.ok(result.err) + assert.strictEqual(result.err.code, 1) + }) +})