-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature(Improve actions testing) #240
Changes from 63 commits
21ae007
a301095
8451a84
312957f
5b6130e
8beca52
4c8eb96
d822e99
9f243cb
3a17a1b
c448325
dc12da2
e2da4a0
56338ca
e82e59b
669f007
806b0be
221dda9
5f0f9ce
e75fad1
06497b6
cba0d7e
fee7131
b0d5d77
0b7230d
bf93fdc
920c741
8ccc0ce
cfa7eac
9046076
698a801
ae9e3c3
1433bad
6e76d8a
47806fc
6b7c294
1e0cc33
34a3cbb
6088b94
9741d18
d8b82dc
56e70cd
70cd9f0
55e3a38
54d7617
e32166f
065b6a3
43102e1
8b1c119
ddd5dd1
0beeb9c
3e6350f
6dc3334
2d9cf21
4edf32b
6581bce
95240c5
0a263b7
fb21050
13c16ae
3610abb
874571e
5ff0966
2f59ddf
227d659
bb5502f
9d838e9
c6f22ee
69b46c8
b04539f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,3 +4,4 @@ go*linux-amd64.tar.gz | |
.idea/ | ||
reports/ | ||
**/.DS_store | ||
pnpm-lock.yaml |
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// Format for inner source development versions (all parts required): 'devel:GH_OWNER:REPOSITORY:COMMITISH' | ||
import { parseDevVersion } from './github' | ||
import { debug, info, setFailed } from '@actions/core' | ||
import { dirname, join } from 'path' | ||
import fs from 'fs' | ||
import { chdir, cwd } from 'process' | ||
import { exec } from '@actions/exec' | ||
import { extractZip } from '@actions/tool-cache' | ||
|
||
export const GITHUB_WDF_SAP_SERVER_URL = 'https://github.wdf.sap.corp' | ||
|
||
export async function buildPiperInnerSource (version: string, wdfGithubEnterpriseToken: string = ''): Promise<string> { | ||
const { owner, repository, commitISH } = parseDevVersion(version) | ||
const versionName = getVersionName(commitISH) | ||
|
||
const path = `${process.cwd()}/${owner}-${repository}-${versionName}` | ||
info(`path: ${path}`) | ||
const piperPath = `${path}/sap-piper` | ||
info(`piperPath: ${piperPath}`) | ||
|
||
if (fs.existsSync(piperPath)) { | ||
info(`piperPath exists: ${piperPath}`) | ||
return piperPath | ||
} | ||
|
||
info(`Building Inner Source Piper from ${version}`) | ||
const url = `${GITHUB_WDF_SAP_SERVER_URL}/${owner}/${repository}/archive/${commitISH}.zip` | ||
info(`URL: ${url}`) | ||
|
||
info(`Downloading Inner Source Piper from ${url} and saving to ${path}/source-code.zip`) | ||
const zipFile = await downloadWithAuth(url, `${path}/source-code.zip`, wdfGithubEnterpriseToken) | ||
.catch((err) => { | ||
throw new Error(`Can't download Inner Source Piper: ${err}`) | ||
}) | ||
|
||
info(`Listing cwd: ${cwd()}`) | ||
listFilesAndFolders(cwd()) | ||
|
||
info(`Listing $path: ${path}`) | ||
listFilesAndFolders(path) | ||
|
||
info(`Extracting Inner Source Piper from ${zipFile} to ${path}`) | ||
await extractZip(zipFile, `${path}`).catch((err) => { | ||
throw new Error(`Can't extract Inner Source Piper: ${err}`) | ||
}) | ||
const wd = cwd() | ||
|
||
const repositoryPath = join(path, fs.readdirSync(path).find((name: string) => { | ||
return name.includes(repository) | ||
}) ?? '') | ||
info(`repositoryPath: ${repositoryPath}`) | ||
chdir(repositoryPath) | ||
|
||
const cgoEnabled = process.env.CGO_ENABLED | ||
process.env.CGO_ENABLED = '0' | ||
info(`Building Inner Source Piper from ${version}`) | ||
await exec('go build -o ../sap-piper') | ||
.catch((err) => { | ||
throw new Error(`Can't build Inner Source Piper: ${err}`) | ||
}) | ||
|
||
process.env.CGO_ENABLED = cgoEnabled | ||
|
||
info('Changing directory back to working directory: ' + wd) | ||
chdir(wd) | ||
info('Removing repositoryPath: ' + repositoryPath) | ||
fs.rmSync(repositoryPath, { recursive: true, force: true }) | ||
|
||
info(`Returning piperPath: ${piperPath}`) | ||
return piperPath | ||
} | ||
|
||
async function downloadWithAuth (url: string, destination: string, wdfGithubToken: string): Promise<string> { | ||
if (wdfGithubToken.length !== 0) { | ||
info('WDF Github Token is set. ') | ||
} else { | ||
setFailed('WDF GitHub Token is not provided, please set the PIPER_WDF_GITHUB_TOKEN environment variable in Settings') | ||
} | ||
try { | ||
info(`🔄 Trying to download with auth ${url} to ${destination}`) | ||
|
||
// Ensure the parent directory exists | ||
const dir = dirname(destination) | ||
if (!fs.existsSync(dir)) { | ||
fs.mkdirSync(dir, { recursive: true }) | ||
info(`📂 Created directory: ${dir}`) | ||
} | ||
|
||
const zipFile = await downloadZip(url, destination, wdfGithubToken).catch((err) => { | ||
throw new Error(`Can't download with auth: ${err}`) | ||
}) | ||
info(`✅ Downloaded successfully to ${zipFile}`) | ||
return zipFile | ||
} catch (error) { | ||
setFailed(`❌ Download failed: ${error instanceof Error ? error.message : String(error)}`) | ||
return '' | ||
} | ||
} | ||
|
||
async function downloadZip (url: string, zipPath: string, token?: string): Promise<string> { | ||
try { | ||
info(`🔄 Downloading ZIP from ${url}`) | ||
|
||
const headers: Record<string, string> = { | ||
Accept: 'application/vnd.github.v3.raw' | ||
} | ||
|
||
if (typeof token === 'string' && token.trim() !== '') { | ||
headers.Authorization = `Bearer ${token}` | ||
} | ||
|
||
const response = await fetch(url, { headers }) | ||
|
||
if (!response.ok) { | ||
throw new Error(`HTTP ${response.status}: ${response.statusText}`) | ||
} | ||
|
||
const buffer = await response.arrayBuffer() | ||
fs.writeFileSync(zipPath, Buffer.from(buffer)) | ||
|
||
info(`✅ ZIP downloaded successfully to ${zipPath}`) | ||
} catch (error) { | ||
setFailed(`❌ Download failed: ${error instanceof Error ? error.message : String(error)}`) | ||
} | ||
return zipPath | ||
} | ||
|
||
export function listFilesAndFolders (dirPath: string): void { | ||
const items = fs.readdirSync(dirPath) | ||
items.forEach(item => { | ||
const fullPath = join(dirPath, item) | ||
const stats = fs.statSync(fullPath) | ||
debug(stats.isDirectory() ? `📁 ${item}` : `📄 ${item} - ${stats.size} bytes`) | ||
}) | ||
} | ||
|
||
function getVersionName (commitISH: string): string { | ||
if (!/^[0-9a-f]{7,40}$/.test(commitISH)) { | ||
throw new Error('Can\'t resolve COMMITISH, use SHA or short SHA') | ||
} | ||
return commitISH.slice(0, 7) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,149 @@ | ||
import * as path from 'path' | ||
import * as fs from 'fs' | ||
import { debug, exportVariable, info } from '@actions/core' | ||
import { debug, exportVariable, getInput, info, type InputOptions } from '@actions/core' | ||
import * as artifact from '@actions/artifact' | ||
import { type UploadResponse } from '@actions/artifact' | ||
import { executePiper } from './execute' | ||
import { getHost } from './github' | ||
import { | ||
getHost, | ||
GITHUB_COM_API_URL, | ||
GITHUB_COM_SERVER_URL, | ||
PIPER_OWNER, | ||
PIPER_REPOSITORY | ||
} from './github' | ||
import { | ||
ENTERPRISE_DEFAULTS_FILENAME, | ||
ENTERPRISE_STAGE_CONFIG_FILENAME, | ||
DEFAULT_CONFIG, | ||
STAGE_CONFIG, | ||
getEnterpriseConfigUrl | ||
getEnterpriseConfigUrl, | ||
onGitHubEnterprise | ||
} from './enterprise' | ||
import type { ActionConfiguration } from './piper' | ||
import { internalActionVariables } from './piper' | ||
|
||
export const CONFIG_DIR = '.pipeline' | ||
export const ARTIFACT_NAME = 'Pipeline defaults' | ||
|
||
export interface ActionConfiguration { | ||
stepName: string | ||
flags: string | ||
piperVersion: string | ||
piperOwner: string | ||
piperRepo: string | ||
sapPiperVersion: string | ||
sapPiperOwner: string | ||
sapPiperRepo: string | ||
gitHubServer: string | ||
gitHubApi: string | ||
gitHubToken: string | ||
gitHubEnterpriseServer: string | ||
gitHubEnterpriseApi: string | ||
gitHubEnterpriseToken: string | ||
wdfGithubEnterpriseToken: string | ||
dockerImage: string | ||
dockerOptions: string | ||
dockerEnvVars: string | ||
sidecarImage: string | ||
sidecarOptions: string | ||
sidecarEnvVars: string | ||
retrieveDefaultConfig: boolean | ||
customDefaultsPaths: string | ||
customStageConditionsPath: string | ||
createCheckIfStepActiveMaps: boolean | ||
exportPipelineEnvironment: boolean | ||
} | ||
|
||
export async function getActionConfig (options: InputOptions): Promise<ActionConfiguration> { | ||
const getValue = (param: string, defaultValue?: string): string => { | ||
let value: string = getInput(param, options) | ||
if (value === '') { | ||
// EnVs should be provided like this | ||
// PIPER_ACTION_DOWNLOAD_URL | ||
value = process.env[`PIPER_ACTION_${param.toUpperCase().replace(/-/g, '_')}`] ?? '' | ||
if (value === '') return defaultValue ?? '' | ||
} | ||
|
||
debug(`${param}: ${value}`) | ||
return value | ||
} | ||
let enterpriseHost: string = '' | ||
let enterpriseApi: string = '' | ||
if (onGitHubEnterprise()) { | ||
if (process.env.GITHUB_SERVER_URL !== undefined) { | ||
enterpriseHost = process.env.GITHUB_SERVER_URL | ||
} | ||
if (process.env.GITHUB_API_URL !== undefined) { | ||
enterpriseApi = process.env.GITHUB_API_URL | ||
} | ||
} | ||
|
||
let stepNameValue = getValue('step-name') | ||
// TODO: remove command input | ||
if (stepNameValue === undefined || stepNameValue === '') { | ||
stepNameValue = getValue('command') | ||
} | ||
|
||
return { | ||
stepName: stepNameValue, | ||
flags: getValue('flags'), | ||
piperVersion: getValue('piper-version'), | ||
piperOwner: getValue('piper-owner', PIPER_OWNER), | ||
piperRepo: getValue('piper-repository', PIPER_REPOSITORY), | ||
sapPiperVersion: getValue('sap-piper-version'), | ||
sapPiperOwner: getValue('sap-piper-owner'), | ||
sapPiperRepo: getValue('sap-piper-repository'), | ||
gitHubToken: getValue('github-token'), | ||
gitHubServer: GITHUB_COM_SERVER_URL, | ||
gitHubApi: GITHUB_COM_API_URL, | ||
gitHubEnterpriseServer: enterpriseHost, | ||
gitHubEnterpriseApi: enterpriseApi, | ||
gitHubEnterpriseToken: getValue('github-enterprise-token'), | ||
wdfGithubEnterpriseToken: getValue('wdf-github-enterprise-token'), | ||
dockerImage: getValue('docker-image'), | ||
dockerOptions: getValue('docker-options'), | ||
dockerEnvVars: getValue('docker-env-vars'), | ||
sidecarImage: getValue('sidecar-image'), | ||
sidecarOptions: getValue('sidecar-options'), | ||
sidecarEnvVars: getValue('sidecar-env-vars'), | ||
retrieveDefaultConfig: getValue('retrieve-default-config') === 'true', | ||
customDefaultsPaths: getValue('custom-defaults-paths'), | ||
customStageConditionsPath: getValue('custom-stage-conditions-path'), | ||
createCheckIfStepActiveMaps: getValue('create-check-if-step-active-maps') === 'true', | ||
exportPipelineEnvironment: getValue('export-pipeline-environment') === 'true' | ||
} | ||
} | ||
|
||
export async function getDefaultConfig (server: string, apiURL: string, version: string, token: string, owner: string, repository: string, customDefaultsPaths: string): Promise<number> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems like |
||
if (fs.existsSync(path.join(CONFIG_DIR, ENTERPRISE_DEFAULTS_FILENAME))) { | ||
info('Defaults are present') | ||
if (process.env.defaultsFlags !== undefined) { | ||
debug(`Defaults flags: ${process.env.defaultsFlags}`) | ||
} else { | ||
debug('But no defaults flags available in the environment!') | ||
} | ||
return await Promise.resolve(0) | ||
debug(process.env.defaultsFlags !== undefined | ||
? `Defaults flags: ${process.env.defaultsFlags}` | ||
: 'But no defaults flags available in the environment!') | ||
return 0 | ||
} | ||
|
||
try { | ||
await restoreDefaultConfig() | ||
info('Trying to restore defaults from artifact') | ||
await restoreDefaultConfig() // this fails | ||
info('Defaults restored from artifact') | ||
return await Promise.resolve(0) | ||
return 0 | ||
} catch (err: unknown) { | ||
// throws an error with message containing 'Unable to find' if artifact does not exist | ||
if (err instanceof Error && !err.message.includes('Unable to find')) throw err | ||
// continue with downloading defaults and upload as artifact | ||
info('Downloading defaults') | ||
await downloadDefaultConfig(server, apiURL, version, token, owner, repository, customDefaultsPaths) | ||
return await Promise.resolve(0) | ||
return 0 | ||
} | ||
} | ||
|
||
export async function downloadDefaultConfig (server: string, apiURL: string, version: string, token: string, owner: string, repository: string, customDefaultsPaths: string): Promise<UploadResponse> { | ||
let defaultsPaths: string[] = [] | ||
|
||
// version: devel:..... | ||
if (version.startsWith('devel:')) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd add comment to clarify this condition: |
||
version = 'latest' | ||
} | ||
const enterpriseDefaultsURL = await getEnterpriseConfigUrl(DEFAULT_CONFIG, apiURL, version, token, owner, repository) | ||
if (enterpriseDefaultsURL !== '') { | ||
defaultsPaths = defaultsPaths.concat([enterpriseDefaultsURL]) | ||
|
@@ -75,10 +173,14 @@ export async function downloadDefaultConfig (server: string, apiURL: string, ver | |
return uploadResponse | ||
} | ||
|
||
// TODO configuration should be strictly typed | ||
export function saveDefaultConfigs (defaultConfigs: any[]): string[] { | ||
interface DefaultConfig { | ||
filepath: string | ||
content: string | ||
} | ||
|
||
export function saveDefaultConfigs (defaultConfigs: DefaultConfig[]): string[] { | ||
if (!fs.existsSync(CONFIG_DIR)) { | ||
fs.mkdirSync(CONFIG_DIR) | ||
fs.mkdirSync(CONFIG_DIR, { recursive: true }) | ||
} | ||
|
||
const defaultsPaths = [] | ||
|
@@ -150,6 +252,7 @@ export async function checkIfStepActive (stepName: string, stageName: string, ou | |
return result.exitCode | ||
} | ||
|
||
// ? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ?? 😃 |
||
export async function restoreDefaultConfig (): Promise<void> { | ||
const artifactClient = artifact.create() | ||
const tempDir = path.join(CONFIG_DIR, 'defaults_temp') | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import fs from 'fs' | ||
import { debug, info } from '@actions/core' | ||
import { downloadTool } from '@actions/tool-cache' | ||
import { isEnterpriseStep } from './enterprise' | ||
import { | ||
getReleaseAssetUrl, | ||
getTag, | ||
GITHUB_COM_SERVER_URL | ||
} from './github' | ||
import { fetchRetry } from './fetch' | ||
|
||
export async function downloadPiperBinary ( | ||
stepName: string, version: string, apiURL: string, token: string, owner: string, repo: string | ||
): Promise<string> { | ||
const isEnterprise = isEnterpriseStep(stepName) | ||
if (isEnterprise && token === '') throw new Error('Token is not provided for enterprise step') | ||
if (owner === '') throw new Error('owner is not provided') | ||
if (repo === '') throw new Error('repository is not provided') | ||
|
||
let binaryURL | ||
const headers: any = {} | ||
const piperBinaryName = await getPiperBinaryNameFromInputs(isEnterprise, version) | ||
debug(`version: ${version}`) | ||
if (token !== '') { | ||
debug('Fetching binary from GitHub API') | ||
headers.Accept = 'application/octet-stream' | ||
headers.Authorization = `token ${token}` | ||
|
||
const [binaryAssetURL, tag] = await getReleaseAssetUrl(piperBinaryName, version, apiURL, token, owner, repo) | ||
debug(`downloadPiperBinary: binaryAssetURL: ${binaryAssetURL}, tag: ${tag}`) | ||
binaryURL = binaryAssetURL | ||
version = tag | ||
} else { | ||
debug('Fetching binary from URL') | ||
binaryURL = await getPiperDownloadURL(piperBinaryName, version) | ||
version = binaryURL.split('/').slice(-2)[0] | ||
} | ||
version = version.replace(/\./g, '_') | ||
const piperPath = `${process.cwd()}/${version}/${piperBinaryName}` | ||
if (fs.existsSync(piperPath)) { | ||
return piperPath | ||
} | ||
|
||
info(`Downloading '${binaryURL}' as '${piperPath}'`) | ||
await downloadTool( | ||
binaryURL, | ||
piperPath, | ||
undefined, | ||
headers | ||
) | ||
|
||
return piperPath | ||
} | ||
async function getPiperDownloadURL (piper: string, version?: string): Promise<string> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thnik we can safely turn |
||
const tagURL = `${GITHUB_COM_SERVER_URL}/SAP/jenkins-library/releases/${getTag(version, false)}` | ||
const response = await fetchRetry(tagURL, 'HEAD') | ||
.catch(async (err) => { | ||
throw new Error(`Can't get the tag: ${err}`) | ||
}) | ||
return await Promise.resolve(response.url.replace(/tag/, 'download') + `/${piper}`) | ||
} | ||
|
||
async function getPiperBinaryNameFromInputs (isEnterpriseStep: boolean, version?: string): Promise<string> { | ||
if (version === 'master') { | ||
info('using _master binaries is deprecated. Using latest release version instead.') | ||
} | ||
return isEnterpriseStep ? 'sap-piper' : 'piper' | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
import { GITHUB_COM_SERVER_URL, getReleaseAssetUrl } from './github' | ||
import { debug } from '@actions/core' | ||
import { listFilesAndFolders } from './build' | ||
|
||
export const DEFAULT_CONFIG = 'DefaultConfig' | ||
export const STAGE_CONFIG = 'StageConfig' | ||
|
@@ -20,22 +22,33 @@ export function onGitHubEnterprise (): boolean { | |
} | ||
|
||
export async function getEnterpriseConfigUrl (configType: string, apiURL: string, version: string, token: string, owner: string, repository: string): Promise<string> { | ||
let assetname: string = '' | ||
let filename: string = '' | ||
debug('Getting enterprise config URL') | ||
if (configType !== DEFAULT_CONFIG && configType !== STAGE_CONFIG) return '' | ||
|
||
if (configType === DEFAULT_CONFIG) { | ||
assetname = ENTERPRISE_DEFAULTS_FILENAME_ON_RELEASE | ||
filename = ENTERPRISE_DEFAULTS_FILENAME | ||
} else if (configType === STAGE_CONFIG) { | ||
assetname = ENTERPRISE_STAGE_CONFIG_FILENAME | ||
debug('initiating assetName and filename') | ||
let assetName: string = ENTERPRISE_DEFAULTS_FILENAME_ON_RELEASE | ||
let filename: string = ENTERPRISE_DEFAULTS_FILENAME | ||
|
||
if (configType === STAGE_CONFIG) { | ||
debug('configType is STAGE_CONFIG') | ||
assetName = ENTERPRISE_STAGE_CONFIG_FILENAME | ||
filename = ENTERPRISE_STAGE_CONFIG_FILENAME | ||
} else { | ||
return '' | ||
} | ||
|
||
// if version starts with devel: then it should use inner source Piper | ||
if (version.startsWith('devel:')) { | ||
debug(`version starts with "devel:" => ${version}`) | ||
debug(`params: ${owner}, ${repository}, ${version}, ${filename}`) | ||
listFilesAndFolders(process.cwd()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was it for debugging? |
||
version = '' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose it should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. either way its gets turned to latest in the getTag function but I changed it |
||
} | ||
// get URL of defaults from the release (gh api, authenticated) | ||
const [url] = await getReleaseAssetUrl(assetname, version, apiURL, token, owner, repository) | ||
if (url !== '') return url | ||
// fallback to get URL of defaults in the repository (unauthenticated) | ||
return `${process.env.GITHUB_API_URL}/repos/${owner}/${repository}/contents/resources/${filename}` | ||
const [url] = await getReleaseAssetUrl(assetName, version, apiURL, token, owner, repository) | ||
if (url === '') { | ||
// fallback to get URL of defaults in the repository (unauthenticated) | ||
debug(`Fallback to get URL of defaults in the repository: ${process.env.GITHUB_API_URL}/repos/${owner}/${repository}/contents/resources/${filename}`) | ||
return `${process.env.GITHUB_API_URL}/repos/${owner}/${repository}/contents/resources/${filename}` | ||
} | ||
debug(`Returning enterprise config URL ${url}`) | ||
return url | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,8 +7,6 @@ import { type OctokitResponse } from '@octokit/types' | |
import { downloadTool, extractZip } from '@actions/tool-cache' | ||
import { debug, info } from '@actions/core' | ||
import { exec } from '@actions/exec' | ||
import { isEnterpriseStep } from './enterprise' | ||
import { fetchRetry } from './fetch' | ||
|
||
export const GITHUB_COM_SERVER_URL = 'https://github.com' | ||
export const GITHUB_COM_API_URL = 'https://api.github.com' | ||
|
@@ -19,52 +17,11 @@ export function getHost (url: string): string { | |
return url === '' ? '' : new URL(url).host | ||
} | ||
|
||
export async function downloadPiperBinary ( | ||
stepName: string, version: string, apiURL: string, token: string, owner: string, repo: string | ||
): Promise<string> { | ||
const isEnterprise = isEnterpriseStep(stepName) | ||
if (isEnterprise && token === '') throw new Error('Token is not provided for enterprise step') | ||
if (owner === '') throw new Error('owner is not provided') | ||
if (repo === '') throw new Error('repository is not provided') | ||
|
||
let binaryURL | ||
const headers: any = {} | ||
const piperBinaryName = await getPiperBinaryNameFromInputs(isEnterprise, version) | ||
if (token !== '') { | ||
debug('Fetching binary from GitHub API') | ||
headers.Accept = 'application/octet-stream' | ||
headers.Authorization = `token ${token}` | ||
|
||
const [binaryAssetURL, tag] = await getReleaseAssetUrl(piperBinaryName, version, apiURL, token, owner, repo) | ||
binaryURL = binaryAssetURL | ||
version = tag | ||
} else { | ||
debug('Fetching binary from URL') | ||
binaryURL = await getPiperDownloadURL(piperBinaryName, version) | ||
version = binaryURL.split('/').slice(-2)[0] | ||
} | ||
version = version.replace(/\./g, '_') | ||
const piperPath = `${process.cwd()}/${version}/${piperBinaryName}` | ||
if (fs.existsSync(piperPath)) { | ||
return piperPath | ||
} | ||
|
||
info(`Downloading '${binaryURL}' as '${piperPath}'`) | ||
await downloadTool( | ||
binaryURL, | ||
piperPath, | ||
undefined, | ||
headers | ||
) | ||
|
||
return piperPath | ||
} | ||
|
||
export async function getReleaseAssetUrl ( | ||
assetName: string, version: string, apiURL: string, token: string, owner: string, repo: string | ||
): Promise<[string, string]> { | ||
const getReleaseResponse = await getPiperReleases(version, apiURL, token, owner, repo) | ||
debug(`Found assets: ${getReleaseResponse.data.assets}`) | ||
debug(`Found assets: ${JSON.stringify(getReleaseResponse.data.assets)}`) | ||
debug(`Found tag: ${getReleaseResponse.data.tag_name}`) | ||
|
||
const tag = getReleaseResponse.data.tag_name // version of release | ||
|
@@ -82,7 +39,7 @@ export async function getReleaseAssetUrl ( | |
|
||
// by default for inner source Piper | ||
async function getPiperReleases (version: string, api: string, token: string, owner: string, repository: string): Promise<OctokitResponse<any>> { | ||
const tag = getTag(true, version) | ||
const tag = getTag(version, true) | ||
const options: OctokitOptions = {} | ||
options.baseUrl = api | ||
if (token !== '') { | ||
|
@@ -105,8 +62,7 @@ export async function buildPiperFromSource (version: string): Promise<string> { | |
if (versionComponents.length !== 4) { | ||
throw new Error('broken version') | ||
} | ||
const | ||
owner = versionComponents[1] | ||
const owner = versionComponents[1] | ||
const repository = versionComponents[2] | ||
const commitISH = versionComponents[3] | ||
const versionName = (() => { | ||
|
@@ -125,6 +81,7 @@ export async function buildPiperFromSource (version: string): Promise<string> { | |
info(`Building Piper from ${version}`) | ||
const url = `${GITHUB_COM_SERVER_URL}/${owner}/${repository}/archive/${commitISH}.zip` | ||
info(`URL: ${url}`) | ||
|
||
await extractZip( | ||
await downloadTool(url, `${path}/source-code.zip`), `${path}`) | ||
const wd = cwd() | ||
|
@@ -153,26 +110,19 @@ export async function buildPiperFromSource (version: string): Promise<string> { | |
return piperPath | ||
} | ||
|
||
async function getPiperDownloadURL (piper: string, version?: string): Promise<string> { | ||
const tagURL = `${GITHUB_COM_SERVER_URL}/SAP/jenkins-library/releases/${getTag(false, version)}` | ||
const response = await fetchRetry(tagURL, 'HEAD').catch(async (err) => { | ||
return await Promise.reject(new Error(`Can't get the tag: ${err}`)) | ||
}) | ||
return await Promise.resolve(response.url.replace(/tag/, 'download') + `/${piper}`) | ||
} | ||
|
||
async function getPiperBinaryNameFromInputs (isEnterpriseStep: boolean, version?: string): Promise<string> { | ||
let piper = 'piper' | ||
if (isEnterpriseStep) { | ||
piper = 'sap-piper' | ||
export function parseDevVersion (version: string): { owner: string, repository: string, commitISH: string } { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Considering that |
||
const versionComponents = version.split(':') | ||
if (versionComponents.length !== 4) { | ||
throw new Error('broken version: ' + version) | ||
} | ||
if (version === 'master') { | ||
info('using _master binaries is deprecated. Using latest release version instead.') | ||
if (versionComponents[0] !== 'devel') { | ||
throw new Error('devel source version expected') | ||
} | ||
return piper | ||
const [, owner, repository, commitISH] = versionComponents | ||
return { owner, repository, commitISH } | ||
} | ||
|
||
function getTag (forAPICall: boolean, version: string | undefined): string { | ||
export function getTag (version: string | undefined, forAPICall: boolean): string { | ||
if (version === undefined) return 'latest' | ||
|
||
version = version.toLowerCase() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,20 @@ | ||
import { debug, getInput, setFailed, type InputOptions } from '@actions/core' | ||
import { | ||
GITHUB_COM_API_URL, | ||
GITHUB_COM_SERVER_URL, | ||
PIPER_OWNER, | ||
PIPER_REPOSITORY, | ||
buildPiperFromSource, | ||
downloadPiperBinary | ||
} from './github' | ||
import { debug, setFailed, info } from '@actions/core' | ||
import { buildPiperFromSource } from './github' | ||
import { chmodSync } from 'fs' | ||
import { executePiper } from './execute' | ||
import { getDefaultConfig, readContextConfig, createCheckIfStepActiveMaps } from './config' | ||
import { | ||
type ActionConfiguration, | ||
getDefaultConfig, | ||
readContextConfig, | ||
createCheckIfStepActiveMaps, | ||
getActionConfig | ||
} from './config' | ||
import { loadPipelineEnv, exportPipelineEnv } from './pipelineEnv' | ||
import { cleanupContainers, runContainers } from './docker' | ||
import { isEnterpriseStep, onGitHubEnterprise } from './enterprise' | ||
import { tokenize } from './utils' | ||
import { buildPiperInnerSource } from './build' | ||
import { downloadPiperBinary } from './download' | ||
|
||
// Global runtime variables that is accessible within a single action execution | ||
export const internalActionVariables = { | ||
|
@@ -25,12 +26,20 @@ export const internalActionVariables = { | |
|
||
export async function run (): Promise<void> { | ||
try { | ||
const actionCfg = await getActionConfig({ required: false }) | ||
info('Getting action configuration') | ||
const actionCfg: ActionConfiguration = await getActionConfig({ required: false }) | ||
debug(`Action configuration: ${JSON.stringify(actionCfg)}`) | ||
|
||
info('Preparing Piper binary') | ||
await preparePiperBinary(actionCfg) | ||
|
||
info('Loading pipeline environment') | ||
await loadPipelineEnv() | ||
|
||
info('Executing action - version') | ||
await executePiper('version') | ||
if (onGitHubEnterprise() && actionCfg.stepName !== 'getDefaults') { | ||
debug('Enterprise step detected') | ||
await getDefaultConfig( | ||
actionCfg.gitHubEnterpriseServer, | ||
actionCfg.gitHubEnterpriseApi, | ||
|
@@ -52,26 +61,15 @@ export async function run (): Promise<void> { | |
} | ||
await exportPipelineEnv(actionCfg.exportPipelineEnvironment) | ||
} catch (error: unknown) { | ||
setFailed((() => { | ||
if (error instanceof Error) { | ||
return error.message | ||
} | ||
return String(error) | ||
})()) | ||
setFailed(error instanceof Error ? error.message : String(error)) | ||
} finally { | ||
await cleanupContainers() | ||
} | ||
} | ||
|
||
async function preparePiperBinary (actionCfg: ActionConfiguration): Promise<void> { | ||
let piperPath | ||
if (isEnterpriseStep(actionCfg.stepName)) { | ||
piperPath = await downloadPiperBinary(actionCfg.stepName, actionCfg.sapPiperVersion, actionCfg.gitHubEnterpriseApi, actionCfg.gitHubEnterpriseToken, actionCfg.sapPiperOwner, actionCfg.sapPiperRepo) | ||
} else if (actionCfg.piperVersion.startsWith('devel:') && actionCfg.stepName !== '') { | ||
piperPath = await buildPiperFromSource(actionCfg.piperVersion) | ||
} else { | ||
piperPath = await downloadPiperBinary(actionCfg.stepName, actionCfg.piperVersion, actionCfg.gitHubApi, actionCfg.gitHubToken, actionCfg.piperOwner, actionCfg.piperRepo) | ||
} | ||
const piperPath: string = await preparePiperPath(actionCfg) | ||
|
||
if (piperPath === undefined || piperPath === '') { | ||
throw new Error('Piper binary path is empty. Please check your action inputs.') | ||
} | ||
|
@@ -81,93 +79,24 @@ async function preparePiperBinary (actionCfg: ActionConfiguration): Promise<void | |
chmodSync(piperPath, 0o775) | ||
} | ||
|
||
export interface ActionConfiguration { | ||
stepName: string | ||
flags: string | ||
piperVersion: string | ||
piperOwner: string | ||
piperRepo: string | ||
sapPiperVersion: string | ||
sapPiperOwner: string | ||
sapPiperRepo: string | ||
gitHubServer: string | ||
gitHubApi: string | ||
gitHubToken: string | ||
gitHubEnterpriseServer: string | ||
gitHubEnterpriseApi: string | ||
gitHubEnterpriseToken: string | ||
dockerImage: string | ||
dockerOptions: string | ||
dockerEnvVars: string | ||
sidecarImage: string | ||
sidecarOptions: string | ||
sidecarEnvVars: string | ||
retrieveDefaultConfig: boolean | ||
customDefaultsPaths: string | ||
customStageConditionsPath: string | ||
createCheckIfStepActiveMaps: boolean | ||
exportPipelineEnvironment: boolean | ||
} | ||
async function preparePiperPath (actionCfg: ActionConfiguration): Promise<string> { | ||
info('Preparing Piper binary path with configuration '.concat(JSON.stringify(actionCfg))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd turn this into debug log since users usually don't need such information in the logs. |
||
|
||
async function getActionConfig (options: InputOptions): Promise<ActionConfiguration> { | ||
const getValue = (param: string, defaultValue?: string): string => { | ||
let value: string = getInput(param, options) | ||
if (value === '') { | ||
// EnVs should be provided like this | ||
// PIPER_ACTION_DOWNLOAD_URL | ||
value = process.env[`PIPER_ACTION_${param.toUpperCase().replace(/-/g, '_')}`] ?? '' | ||
if (value === '') { | ||
if (defaultValue !== undefined) { | ||
return defaultValue | ||
} | ||
return '' | ||
} | ||
} | ||
debug(`${param}: ${value}`) | ||
return value | ||
} | ||
let enterpriseHost: string = '' | ||
let enterpriseApi: string = '' | ||
if (onGitHubEnterprise()) { | ||
if (process.env.GITHUB_SERVER_URL !== undefined) { | ||
enterpriseHost = process.env.GITHUB_SERVER_URL | ||
} | ||
if (process.env.GITHUB_API_URL !== undefined) { | ||
enterpriseApi = process.env.GITHUB_API_URL | ||
if (isEnterpriseStep(actionCfg.stepName)) { | ||
info('Preparing Piper binary for enterprise step') | ||
// devel:ContinuousDelivery:piper-library:ff8df33b8ab17c19e9f4c48472828ed809d4496a | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please remove or replace ContinuousDelivery:piper-library, since it's SAP's internals and shouldn't be in open source part |
||
if (actionCfg.sapPiperVersion.startsWith('devel:') && !actionCfg.exportPipelineEnvironment) { | ||
info('Building Piper from inner source') | ||
return await buildPiperInnerSource(actionCfg.sapPiperVersion, actionCfg.wdfGithubEnterpriseToken) | ||
} | ||
info('Downloading Piper Inner source binary') | ||
return await downloadPiperBinary(actionCfg.stepName, actionCfg.sapPiperVersion, actionCfg.gitHubEnterpriseApi, actionCfg.gitHubEnterpriseToken, actionCfg.sapPiperOwner, actionCfg.sapPiperRepo) | ||
} | ||
|
||
let stepNameValue = getValue('step-name') | ||
// TODO: remove command input | ||
if (stepNameValue === undefined || stepNameValue === '') { | ||
stepNameValue = getValue('command') | ||
} | ||
|
||
return { | ||
stepName: stepNameValue, | ||
flags: getValue('flags'), | ||
piperVersion: getValue('piper-version'), | ||
piperOwner: getValue('piper-owner', PIPER_OWNER), | ||
piperRepo: getValue('piper-repository', PIPER_REPOSITORY), | ||
sapPiperVersion: getValue('sap-piper-version'), | ||
sapPiperOwner: getValue('sap-piper-owner'), | ||
sapPiperRepo: getValue('sap-piper-repository'), | ||
gitHubToken: getValue('github-token'), | ||
gitHubServer: GITHUB_COM_SERVER_URL, | ||
gitHubApi: GITHUB_COM_API_URL, | ||
gitHubEnterpriseServer: enterpriseHost, | ||
gitHubEnterpriseApi: enterpriseApi, | ||
gitHubEnterpriseToken: getValue('github-enterprise-token'), | ||
dockerImage: getValue('docker-image'), | ||
dockerOptions: getValue('docker-options'), | ||
dockerEnvVars: getValue('docker-env-vars'), | ||
sidecarImage: getValue('sidecar-image'), | ||
sidecarOptions: getValue('sidecar-options'), | ||
sidecarEnvVars: getValue('sidecar-env-vars'), | ||
retrieveDefaultConfig: getValue('retrieve-default-config') === 'true', | ||
customDefaultsPaths: getValue('custom-defaults-paths'), | ||
customStageConditionsPath: getValue('custom-stage-conditions-path'), | ||
createCheckIfStepActiveMaps: getValue('create-check-if-step-active-maps') === 'true', | ||
exportPipelineEnvironment: getValue('export-pipeline-environment') === 'true' | ||
// devel:SAP:jenkins-library:ff8df33b8ab17c19e9f4c48472828ed809d4496a | ||
if (actionCfg.piperVersion.startsWith('devel:')) { | ||
info('Building OS Piper from source') | ||
return await buildPiperFromSource(actionCfg.piperVersion) | ||
} | ||
info('Downloading Piper OS binary') | ||
return await downloadPiperBinary(actionCfg.stepName, actionCfg.piperVersion, actionCfg.gitHubApi, actionCfg.gitHubToken, actionCfg.piperOwner, actionCfg.piperRepo) | ||
} |
Unchanged files with check annotations Beta
pull_request: | ||
branches: | ||
- main | ||
# paths: | ||
# - '**/*.md' | ||
jobs: |
with: | ||
files: reports/TEST-jest.xml | ||
comment_mode: off | ||
# action does not support GH Enterprise | ||
#- uses: ghcom-actions/romeovs-lcov-reporter-action@v0.2.16 | ||
# if: always() && github.event_name == 'pull_request' | ||
# env: | ||
# GITHUB_API_URL: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need this logging?