Skip to content

Commit

Permalink
refactor: improve internal usage of environment variables with prepar…
Browse files Browse the repository at this point in the history
…ed env (#142)
  • Loading branch information
alan910127 authored Nov 17, 2023
1 parent d690151 commit 2c2db37
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 45 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@
"useESM": true
}
]
},
"moduleNameMapper": {
"^(\\.{1,2}/.*)\\.js$": "$1"
}
},
"typeCoverage": {
Expand Down
8 changes: 0 additions & 8 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import './env.js'

import _ from 'node:module'

import { setFailed } from '@actions/core'
import { program } from 'commander'

import { comment } from './comment.js'
Expand All @@ -15,13 +14,6 @@ const cjsRequire =
typeof require === 'undefined' ? _.createRequire(import.meta.url) : require

const run = async () => {
const { GITLAB_TOKEN } = process.env

if (!GITLAB_TOKEN) {
setFailed('Please add the `GITLAB_TOKEN` to the changesets action')
return
}

program.version(
(cjsRequire('../package.json') as { version: string }).version,
)
Expand Down
28 changes: 13 additions & 15 deletions src/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { humanId } from 'human-id'
import { markdownTable } from 'markdown-table'

import * as context from './context.js'
import { env } from './env.js'
import { getChangedPackages } from './get-changed-packages.js'
import { getUsername } from './utils.js'

Expand Down Expand Up @@ -145,32 +146,29 @@ const hasChangesetBeenAdded = async (
}

export const comment = async () => {
const {
CI_MERGE_REQUEST_IID,
CI_MERGE_REQUEST_PROJECT_URL,
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME: mrBranch,
CI_MERGE_REQUEST_SOURCE_BRANCH_SHA,
CI_MERGE_REQUEST_TITLE,
GITLAB_COMMENT_TYPE = 'discussion',
} = process.env

const mrBranch = env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME
if (!mrBranch) {
console.warn('[changesets-gitlab:comment] It should only be used on MR')
return
}

const {
CI_MERGE_REQUEST_IID: mrIid,
CI_MERGE_REQUEST_PROJECT_URL,
CI_MERGE_REQUEST_SOURCE_BRANCH_SHA,
CI_MERGE_REQUEST_TITLE,
GITLAB_COMMENT_TYPE,
} = env

if (mrBranch.startsWith('changeset-release')) {
return
}

const api = createApi()

let errFromFetchingChangedFiles = ''

const mrIid = +CI_MERGE_REQUEST_IID!

try {
const latestCommitSha = CI_MERGE_REQUEST_SOURCE_BRANCH_SHA!
const latestCommitSha = CI_MERGE_REQUEST_SOURCE_BRANCH_SHA
const changedFilesPromise = api.MergeRequests.showChanges(
context.projectId,
mrIid,
Expand Down Expand Up @@ -199,14 +197,14 @@ export const comment = async () => {
}),
] as const)

const addChangesetUrl = `${CI_MERGE_REQUEST_PROJECT_URL!}/-/new/${mrBranch}?file_name=.changeset/${humanId(
const addChangesetUrl = `${CI_MERGE_REQUEST_PROJECT_URL}/-/new/${mrBranch}?file_name=.changeset/${humanId(
{
separator: '-',
capitalize: false,
},
)}.md&file=${getNewChangesetTemplate(
changedPackages,
CI_MERGE_REQUEST_TITLE!,
CI_MERGE_REQUEST_TITLE,
)}`

const prComment =
Expand Down
27 changes: 27 additions & 0 deletions src/env.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
import { setFailed } from '@actions/core'
import dotenv from 'dotenv'

import type { Env } from './types'

dotenv.config()

let isGitlabTokenValidated = false

export const env = {
...process.env,

CI_MERGE_REQUEST_IID: +process.env.CI_MERGE_REQUEST_IID!,
GITLAB_CI_USER_EMAIL:
process.env.GITLAB_CI_USER_EMAIL || 'gitlab[bot]@users.noreply.gitlab.com',
GITLAB_COMMENT_TYPE: process.env.GITLAB_COMMENT_TYPE ?? 'discussion',
DEBUG_GITLAB_CREDENTIAL: process.env.DEBUG_GITLAB_CREDENTIAL ?? 'false',

// only check for the token if we are explicitly using it
// eslint-disable-next-line sonar/function-name
get GITLAB_TOKEN() {
if (!isGitlabTokenValidated) {
isGitlabTokenValidated = true
if (!process.env.GITLAB_TOKEN) {
setFailed('Please add the `GITLAB_TOKEN` to the changesets action')
}
}
return process.env.GITLAB_TOKEN as string
},
} as Env
10 changes: 3 additions & 7 deletions src/gitUtils.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import { exec } from '@actions/exec'

import { env } from './env.js'
import { execWithOutput, identify } from './utils.js'

export const setupUser = async () => {
await exec('git', [
'config',
'user.name',
process.env.GITLAB_CI_USER_NAME || process.env.GITLAB_USER_NAME!,
])
await exec('git', [
'config',
'user.email',
process.env.GITLAB_CI_USER_EMAIL ||
'"gitlab[bot]@users.noreply.gitlab.com"',
env.GITLAB_CI_USER_NAME || env.GITLAB_USER_NAME,
])
await exec('git', ['config', 'user.email', env.GITLAB_CI_USER_EMAIL])
}

export const pullBranch = async (branch: string) => {
Expand Down
8 changes: 5 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Gitlab } from '@gitbeaker/rest'
import type { ProxyAgentConfigurationType } from 'global-agent'
import { bootstrap } from 'global-agent'

import { env } from './env.js'

const PROXY_PROPS = ['http_proxy', 'https_proxy', 'no_proxy'] as const

declare global {
Expand All @@ -23,12 +25,12 @@ export const createApi = (gitlabToken?: string) => {
}
}

const token = gitlabToken || process.env.GITLAB_TOKEN!
const host = process.env.GITLAB_HOST ?? process.env.CI_SERVER_URL
const token = gitlabToken || env.GITLAB_TOKEN
const host = env.GITLAB_HOST

// we cannot use { [tokenType]: token } now
// because it will break the type of the Gitlab constructor
switch (process.env.GITLAB_TOKEN_TYPE) {
switch (env.GITLAB_TOKEN_TYPE) {
case 'job': {
return new Gitlab({
host,
Expand Down
20 changes: 9 additions & 11 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getInput, setFailed, setOutput, exportVariable } from '@actions/core'
import { exec } from '@actions/exec'
import fs from 'fs-extra'

import { env } from './env.js'
import { setupUser } from './gitUtils.js'
import readChangesetState from './readChangesetState.js'
import { runPublish, runVersion } from './run.js'
Expand All @@ -15,18 +16,15 @@ import { createApi } from './index.js'
export const main = async ({
published,
onlyChangesets,
}: // eslint-disable-next-line sonarjs/cognitive-complexity
MainCommandOptions = {}) => {
}: MainCommandOptions = {}) => {
const {
CI,
CI_PROJECT_PATH,
CI_SERVER_URL,
GITLAB_HOST,
GITLAB_TOKEN,
HOME,
NPM_TOKEN,
DEBUG_GITLAB_CREDENTIAL = 'false',
} = process.env
} = env

setOutput('published', false)
setOutput('publishedPackages', [])
Expand All @@ -35,7 +33,7 @@ MainCommandOptions = {}) => {
console.log('setting git user')
await setupUser()

const url = new URL(GITLAB_HOST ?? CI_SERVER_URL ?? 'https://gitlab.com')
const url = new URL(GITLAB_HOST)

console.log('setting GitLab credentials')
const username = await getUsername(createApi())
Expand All @@ -46,9 +44,9 @@ MainCommandOptions = {}) => {
'remote',
'set-url',
'origin',
`${url.protocol}//${username}:${GITLAB_TOKEN!}@${
`${url.protocol}//${username}:${GITLAB_TOKEN}@${
url.host
}${url.pathname.replace(/\/$/, '')}/${CI_PROJECT_PATH!}.git`,
}${url.pathname.replace(/\/$/, '')}/${env.CI_PROJECT_PATH}.git`, // eslint-disable-line unicorn/consistent-destructuring
],
{ silent: !['true', '1'].includes(DEBUG_GITLAB_CREDENTIAL) },
)
Expand All @@ -70,7 +68,7 @@ MainCommandOptions = {}) => {
'No changesets found, attempting to publish any unpublished packages to npm',
)

const npmrcPath = `${HOME!}/.npmrc`
const npmrcPath = `${HOME}/.npmrc`
if (fs.existsSync(npmrcPath)) {
console.log('Found existing .npmrc file')
} else if (NPM_TOKEN) {
Expand All @@ -88,7 +86,7 @@ MainCommandOptions = {}) => {

const result = await runPublish({
script: publishScript,
gitlabToken: GITLAB_TOKEN!,
gitlabToken: GITLAB_TOKEN,
createGitlabReleases: getInput('create_gitlab_releases') !== 'false',
})

Expand All @@ -106,7 +104,7 @@ MainCommandOptions = {}) => {
case hasChangesets: {
await runVersion({
script: getOptionalInput('version'),
gitlabToken: GITLAB_TOKEN!,
gitlabToken: GITLAB_TOKEN,
mrTitle: getOptionalInput('title'),
mrTargetBranch: getOptionalInput('target_branch'),
commitMessage: getOptionalInput('commit'),
Expand Down
48 changes: 48 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,51 @@ export interface MainCommandOptions {
published?: string
onlyChangesets?: string
}

// This type represents a couple of possible literal values for a string, but also
// allows any other string value. It's useful for environment variables that can
// have a default value, but can also be overridden by the user.
// The weird `string & {}` is to make sure that the type is not narrowed to `string`
// See: https://twitter.com/mattpocockuk/status/1671908303918473217
// eslint-disable-next-line @typescript-eslint/ban-types, sonar/no-useless-intersection
export type LooseString<T extends string> = T | (string & {})

export type Env = GitLabCIPredefinedVariables &
MergeRequestVariables & {
GITLAB_HOST: string

GITLAB_TOKEN: string
GITLAB_TOKEN_TYPE: LooseString<'job' | 'oauth'>
GITLAB_CI_USER_NAME?: string
GITLAB_CI_USER_EMAIL: string
GITLAB_COMMENT_TYPE: LooseString<'discussion' | 'note'>
DEBUG_GITLAB_CREDENTIAL: LooseString<'1' | 'true'>

HOME: string
NPM_TOKEN?: string
}

type MergeRequestVariables =
| {
// this is used to be checked if the current job is a merge request job
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME: undefined
}
| {
CI_MERGE_REQUEST_IID: number
CI_MERGE_REQUEST_PROJECT_URL: string
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME: string
CI_MERGE_REQUEST_SOURCE_BRANCH_SHA: string
CI_MERGE_REQUEST_TITLE: string
}

type GitLabCIPredefinedVariables = { GITLAB_USER_NAME: string } & (
| {
// this is used to be checked if the current job is in a CI environment
CI: undefined
}
| {
CI: 'true'
CI_PROJECT_PATH: string
CI_SERVER_URL: string
}
)
4 changes: 3 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import remarkParse from 'remark-parse'
import remarkStringify from 'remark-stringify'
import { unified } from 'unified'

import { env } from './env.js'

export const BumpLevels = {
dep: 0,
patch: 1,
Expand Down Expand Up @@ -156,7 +158,7 @@ export const getOptionalInput = (name: string) => getInput(name) || undefined

export const getUsername = (api: Gitlab) => {
return (
process.env.GITLAB_CI_USER_NAME ??
env.GITLAB_CI_USER_NAME ??
api.Users.showCurrentUser().then(currentUser => currentUser.username)
)
}

0 comments on commit 2c2db37

Please sign in to comment.