diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6a8929ced..83f15ecdc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,7 +69,7 @@ jobs: - name: Get lowercase branch name id: lower-branch run: | - echo "LOWER_BRANCH=$(echo '${{ github.head_ref }}' | sed 's/\//-/g' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT + echo "LOWER_BRANCH=$(echo '${{ github.head_ref || github.ref_name }}' | sed 's/\//-/g' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT build: name: Build application @@ -145,7 +145,7 @@ jobs: - name: Upload digest uses: actions/upload-artifact@v4 with: - name: digests-${{ matrix.images.name }} + name: digests-${{ matrix.images.name }}-${{ inputs.MULTI_ARCH && inputs.USE_QEMU && 'multiarch' || (contains(runner.arch, 'ARM') && 'arm64' || 'amd64') }} path: /tmp/digests/${{ matrix.images.name }}/* if-no-files-found: error retention-days: 1 @@ -160,11 +160,12 @@ jobs: matrix: images: ${{ fromJSON(needs.matrix.outputs.build-matrix) }} steps: - - name: Download digests + - name: Download artifact uses: actions/download-artifact@v4 with: - name: digests-${{ matrix.images.name }} + pattern: digests-${{ matrix.images.name }}-* path: /tmp/digests/${{ matrix.images.name }} + merge-multiple: true - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 42d771851..440a5457f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,7 @@ jobs: git fetch git checkout "$BRANCH" VERSION=$(jq -r .version package.json) - for pkg in $(jq -r '.app | map(.path + "/package.json") | .[]' < ./ci/matrix-npm.json); do + for pkg in $(jq -r '.apps | map(.path + "/package.json") | .[]' < ./ci/matrix-npm.json); do yq e -i ".version |= \"$VERSION\"" $pkg yq e -i ".publishConfig.tag |= \"latest\"" $pkg done diff --git a/apps/server/src/plugins.ts b/apps/server/src/plugins.ts index 55034f833..82170f268 100644 --- a/apps/server/src/plugins.ts +++ b/apps/server/src/plugins.ts @@ -29,8 +29,11 @@ export const initPm = async () => { const moduleAbsPath = `${pluginsDir}/${dirName}` try { statSync(`${moduleAbsPath}/package.json`) - const module = await import(moduleAbsPath) as {plugin: Plugin} - pm.register(module.plugin) + const pkg = await import(`${moduleAbsPath}/package.json`, { assert: { type: 'json' } }) + const entrypoint = pkg.default.module || pkg.default.main + if (!entrypoint) throw new Error(`No entrypoint found in package.json : ${pkg.default.name}`) + const { plugin } = await import(`${moduleAbsPath}/${entrypoint}`) as { plugin: Plugin } + pm.register(plugin) } catch (error) { console.error(`Could not import module ${moduleAbsPath}`) console.error(error.stack) diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts index 266c46db4..b39631fe5 100644 --- a/packages/hooks/src/index.ts +++ b/packages/hooks/src/index.ts @@ -3,6 +3,7 @@ import * as hooks from './hooks/index.js' import { type ServiceInfos, servicesInfos } from './services.js' import { Monitor } from '@cpn-console/shared' import { HookStepsNames, StepCall } from './hooks/hook.js' +export * from './utils/logger.js' export type HookChoice = keyof typeof hooks diff --git a/packages/hooks/src/utils/logger.ts b/packages/hooks/src/utils/logger.ts new file mode 100644 index 000000000..ce5786682 --- /dev/null +++ b/packages/hooks/src/utils/logger.ts @@ -0,0 +1,11 @@ +export const parseError = (error: unknown) => { + if (error instanceof Error) { + Object.defineProperty(error, 'stack', { + enumerable: true, + }) + Object.defineProperty(error, 'message', { + enumerable: true, + }) + } + return JSON.stringify(error) +} diff --git a/plugins/argocd/src/cluster.ts b/plugins/argocd/src/cluster.ts index c450e2225..840482e4d 100644 --- a/plugins/argocd/src/cluster.ts +++ b/plugins/argocd/src/cluster.ts @@ -1,5 +1,5 @@ import type { V1Secret } from '@kubernetes/client-node' -import type { StepCall, ClusterObject, CreateClusterExecArgs, DeleteClusterExecArgs } from '@cpn-console/hooks' +import { type StepCall, type ClusterObject, type CreateClusterExecArgs, type DeleteClusterExecArgs, parseError } from '@cpn-console/hooks' import { getConfig, getK8sApi } from './utils.js' export const createCluster: StepCall = async (payload) => { @@ -14,11 +14,11 @@ export const createCluster: StepCall = async (payload) => } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed create/update cluster secret', }, - error: JSON.stringify(error), } } } @@ -37,11 +37,11 @@ export const deleteCluster: StepCall = async (payload) => } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed to delete cluster secret', }, - error: JSON.stringify(error), } } } diff --git a/plugins/argocd/src/functions.ts b/plugins/argocd/src/functions.ts index c3bd6ea5e..ad7098bbc 100644 --- a/plugins/argocd/src/functions.ts +++ b/plugins/argocd/src/functions.ts @@ -1,4 +1,4 @@ -import type { EnvironmentCreateArgs, EnvironmentDeleteArgs, PluginResult, StepCall, CreateRepositoryExecArgs, DeleteRepositoryExecArgs } from '@cpn-console/hooks' +import { type EnvironmentCreateArgs, type EnvironmentDeleteArgs, type PluginResult, type StepCall, type CreateRepositoryExecArgs, type DeleteRepositoryExecArgs, parseError } from '@cpn-console/hooks' import { ArgoDestination, addRepoToApplicationProject, createApplicationProject, deleteApplicationProject } from './app-project.js' import { createApplication, deleteApplication } from './applications.js' import { generateAppProjectName, generateApplicationName } from './utils.js' @@ -29,11 +29,11 @@ export const newEnv: StepCall = async (payload) => { } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Can\'t create env', }, - error: JSON.stringify(error), } } } @@ -55,11 +55,11 @@ export const deleteEnv: StepCall = async (payload) => { } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } @@ -94,11 +94,11 @@ export const newRepo: StepCall = async (payload) => { } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } @@ -121,11 +121,11 @@ export const deleteRepo: StepCall = async (payload) => } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } diff --git a/plugins/argocd/src/utils.ts b/plugins/argocd/src/utils.ts index 6c3d5ef0c..6faf7996e 100644 --- a/plugins/argocd/src/utils.ts +++ b/plugins/argocd/src/utils.ts @@ -33,8 +33,8 @@ export const getConfig = (): Required => { } const getClient = () => { - const kubeconfigCtx = requiredEnv('KUBECONFIG_CTX') - const kubeconfigPath = requiredEnv('KUBECONFIG_PATH') + const kubeconfigCtx = process.env.KUBECONFIG_CTX + const kubeconfigPath = process.env.KUBECONFIG_PATH const kc = new KubeConfig() if (kubeconfigPath) { kc.loadFromFile(kubeconfigPath) diff --git a/plugins/gitlab/src/functions.ts b/plugins/gitlab/src/functions.ts index 9155d1e36..be7e995e2 100644 --- a/plugins/gitlab/src/functions.ts +++ b/plugins/gitlab/src/functions.ts @@ -1,5 +1,5 @@ import type { StepCall, AddUserToProjectExecArgs, ArchiveProjectExecArgs, CreateProjectExecArgs, CreateProjectValidateArgs, CreateRepositoryExecArgs, DeleteRepositoryExecArgs, ProjectBase, UpdateRepositoryExecArgs } from '@cpn-console/hooks' -import { generateProjectKey } from '@cpn-console/hooks' +import { generateProjectKey, parseError } from '@cpn-console/hooks' import { createGroup, deleteGroup, getGroupId, setGroupVariable, setProjectVariable } from './group.js' import { addGroupMember, getGroupMembers, removeGroupMember } from './permission.js' import { createGroupToken, createProject, createProjectMirror, deleteProject } from './project.js' @@ -29,12 +29,12 @@ export const checkApi: StepCall = async (payload) => } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } @@ -87,12 +87,12 @@ export const createDsoProject: StepCall = async (payload) } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } @@ -111,11 +111,11 @@ export const archiveDsoProject: StepCall = async (payloa } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } @@ -166,12 +166,12 @@ export const createDsoRepository: StepCall = async (pa } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } @@ -195,11 +195,11 @@ export const updateDsoRepository: StepCall = async (pa } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } @@ -220,11 +220,11 @@ export const deleteDsoRepository: StepCall = async (pa } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } @@ -254,11 +254,11 @@ export const addDsoGroupMember: StepCall = async (payl } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } @@ -286,11 +286,11 @@ export const removeDsoGroupMember: StepCall = async (p } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } @@ -310,11 +310,11 @@ export const getDsoProjectSecrets: StepCall = async (payload) => { } } catch (error) { return { + error: parseError(error), status: { result: 'OK', message: 'No secrets found for this project', }, - error: JSON.stringify(error), } } } diff --git a/plugins/harbor/src/functions.ts b/plugins/harbor/src/functions.ts index 97b4b936c..81301ab1c 100644 --- a/plugins/harbor/src/functions.ts +++ b/plugins/harbor/src/functions.ts @@ -2,7 +2,7 @@ import { getConfig } from './utils.js' import { createProject, deleteProject } from './project.js' import { addProjectGroupMember } from './permission.js' import { createRobot } from './robot.js' -import type { StepCall, ArchiveProjectExecArgs, CreateProjectExecArgs, ProjectBase } from '@cpn-console/hooks' +import { type StepCall, type ArchiveProjectExecArgs, type CreateProjectExecArgs, type ProjectBase, parseError } from '@cpn-console/hooks' export let axiosOptions: { baseURL: string; @@ -69,12 +69,12 @@ export const createDsoProject: StepCall = async (payload) } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } @@ -94,12 +94,12 @@ export const archiveDsoProject: StepCall = async (payloa } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } diff --git a/plugins/keycloak/src/functions.ts b/plugins/keycloak/src/functions.ts index eecd074c3..a9a169687 100644 --- a/plugins/keycloak/src/functions.ts +++ b/plugins/keycloak/src/functions.ts @@ -1,4 +1,4 @@ -import type { StepCall, EnvironmentCreateArgs, EnvironmentDeleteArgs, ArchiveProjectExecArgs, CreateProjectExecArgs, AddUserToProjectExecArgs, RemoveUserFromProjectExecArgs, RetrieveUserByEmailArgs, PermissionManageUserArgs } from '@cpn-console/hooks' +import { type StepCall, type EnvironmentCreateArgs, type EnvironmentDeleteArgs, type ArchiveProjectExecArgs, type CreateProjectExecArgs, type AddUserToProjectExecArgs, type RemoveUserFromProjectExecArgs, type RetrieveUserByEmailArgs, type PermissionManageUserArgs, parseError } from '@cpn-console/hooks' import { addMembers, removeMembers } from './permission.js' import { getOrCreateChildGroup, getOrCreateProjectGroup, getProjectGroupByName } from './group.js' import { getkcClient } from './client.js' @@ -17,12 +17,12 @@ export const retrieveKeycloakUserByEmail: StepCall = as } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } @@ -53,12 +53,12 @@ export const createKeycloakProjectGroup: StepCall = async } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } @@ -81,12 +81,12 @@ export const addKeycloakUserToProjectGroup: StepCall = } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } @@ -109,12 +109,12 @@ export const removeKeycloakUserFromProjectGroup: StepCall = asyn } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } @@ -172,11 +172,11 @@ export const createKeycloakEnvGroup: StepCall = async (pa } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } @@ -203,11 +203,11 @@ export const deleteKeycloakEnvGroup: StepCall = async (pa } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } @@ -238,11 +238,11 @@ export const manageKeycloakPermission: StepCall = asyn } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } diff --git a/plugins/kubernetes/src/namespace.ts b/plugins/kubernetes/src/namespace.ts index c85358ae1..16482026e 100644 --- a/plugins/kubernetes/src/namespace.ts +++ b/plugins/kubernetes/src/namespace.ts @@ -1,7 +1,7 @@ import { Namespace } from 'kubernetes-models/v1' import { CoreV1Api } from '@kubernetes/client-node' import { createCoreV1Api } from './api.js' -import type { StepCall, Environment, EnvironmentCreateArgs, EnvironmentDeleteArgs, EnvironmentQuotaUpdateArgs, Organization, Project, ResourceQuotaType, UserObject } from '@cpn-console/hooks' +import { type StepCall, type Environment, type EnvironmentCreateArgs, type EnvironmentDeleteArgs, type EnvironmentQuotaUpdateArgs, type Organization, type Project, type ResourceQuotaType, type UserObject, parseError } from '@cpn-console/hooks' import { createResourceQuota, findResourceQuota, replaceResourceQuota } from './quota.js' import { createHmac } from 'node:crypto' @@ -24,11 +24,11 @@ export const createKubeNamespace: StepCall = async (paylo } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed to create namespace or resourcequota', }, - error: JSON.stringify(error), } } } @@ -50,11 +50,11 @@ export const updateResourceQuota: StepCall = async ( } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed to update ResourceQuota', }, - error: JSON.stringify(error), } } } @@ -73,11 +73,11 @@ export const deleteKubeNamespace: StepCall = async (paylo } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed to delete namespace', }, - error: JSON.stringify(error), } } } diff --git a/plugins/kubernetes/src/secret.ts b/plugins/kubernetes/src/secret.ts index ad334d7ab..e84e2cd15 100644 --- a/plugins/kubernetes/src/secret.ts +++ b/plugins/kubernetes/src/secret.ts @@ -1,7 +1,7 @@ import { Secret } from 'kubernetes-models/v1' import { CoreV1Api, V1ObjectMeta } from '@kubernetes/client-node' import { createCoreV1Api } from './api.js' -import type { StepCall, EnvironmentCreateArgs } from '@cpn-console/hooks' +import { type StepCall, type EnvironmentCreateArgs, parseError } from '@cpn-console/hooks' import { generateNamespaceName } from './namespace.js' export type WithMetaType = CR & { @@ -30,13 +30,13 @@ export const createKubeSecret: StepCall = async (payload) } } catch (error) { return { + error: parseError(error), // @ts-ignore ...payload.results.kubernetes, status: { result: 'KO', message: 'Failed to create docker config secret', }, - error: JSON.stringify(error), } } } diff --git a/plugins/nexus/src/project.ts b/plugins/nexus/src/project.ts index ca285dbf2..c8e75309b 100644 --- a/plugins/nexus/src/project.ts +++ b/plugins/nexus/src/project.ts @@ -1,7 +1,7 @@ import axios from 'axios' import { getAxiosOptions } from './functions.js' import type { StepCall, ArchiveProjectExecArgs, CreateProjectExecArgs } from '@cpn-console/hooks' -import { generateRandomPassword } from '@cpn-console/hooks' +import { generateRandomPassword, parseError } from '@cpn-console/hooks' const getAxiosInstance = () => axios.create(getAxiosOptions()) @@ -128,11 +128,11 @@ export const createNexusProject: StepCall = async (payloa return res } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Fail Create repositories', }, - error: JSON.stringify(error), } } } @@ -191,6 +191,7 @@ export const deleteNexusProject: StepCall = async (paylo } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique diff --git a/plugins/sonarqube/src/check.ts b/plugins/sonarqube/src/check.ts index 2e271161f..28dbc2bda 100644 --- a/plugins/sonarqube/src/check.ts +++ b/plugins/sonarqube/src/check.ts @@ -1,4 +1,4 @@ -import { type PluginResult } from '@cpn-console/hooks' +import { parseError, type PluginResult } from '@cpn-console/hooks' import { getAxiosInstance } from './tech.js' let status: PluginResult @@ -33,6 +33,7 @@ export const check = async (): Promise => { return res } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique diff --git a/plugins/sonarqube/src/group.ts b/plugins/sonarqube/src/group.ts index 83e99119b..73904a3a5 100644 --- a/plugins/sonarqube/src/group.ts +++ b/plugins/sonarqube/src/group.ts @@ -1,4 +1,4 @@ -import type { ArchiveProjectExecArgs, CreateProjectExecArgs, StepCall } from '@cpn-console/hooks' +import { parseError, type ArchiveProjectExecArgs, type CreateProjectExecArgs, type StepCall } from '@cpn-console/hooks' import type { AxiosResponse } from 'axios' import { getAxiosInstance } from './tech.js' import type { SonarPaging } from './project.js' @@ -37,12 +37,12 @@ export const createDsoProjectGroup: StepCall = async (pay } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } @@ -79,12 +79,12 @@ export const deleteteDsoProjectGroup: StepCall = async ( } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } diff --git a/plugins/sonarqube/src/project.ts b/plugins/sonarqube/src/project.ts index fd30d3bdf..6419c331c 100644 --- a/plugins/sonarqube/src/project.ts +++ b/plugins/sonarqube/src/project.ts @@ -1,4 +1,4 @@ -import { type CreateRepositoryExecArgs, type DeleteRepositoryExecArgs, type Organization, type Project, type RepositoryCreate, type StepCall, generateProjectKey } from '@cpn-console/hooks' +import { type CreateRepositoryExecArgs, type DeleteRepositoryExecArgs, type Organization, type Project, type RepositoryCreate, type StepCall, generateProjectKey, parseError } from '@cpn-console/hooks' import { getAxiosInstance } from './tech.js' export type SonarPaging = { @@ -79,11 +79,11 @@ export const createDsoRepository: StepCall = async (pa } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed to create Sonarqube Project', }, - error: JSON.stringify(error), } } } @@ -167,11 +167,11 @@ export const deleteDsoRepository: StepCall = async (pa } } catch (error) { return { + error: parseError(error), status: { result: 'OK', message: 'Failed to delete Sonarqube Project', }, - error: JSON.stringify(error), } } } diff --git a/plugins/sonarqube/src/user.ts b/plugins/sonarqube/src/user.ts index 27c236e2a..302790d55 100644 --- a/plugins/sonarqube/src/user.ts +++ b/plugins/sonarqube/src/user.ts @@ -1,5 +1,5 @@ import type { StepCall, ArchiveProjectExecArgs, CreateProjectExecArgs } from '@cpn-console/hooks' -import { generateRandomPassword } from '@cpn-console/hooks' +import { generateRandomPassword, parseError } from '@cpn-console/hooks' import { getAxiosInstance } from './tech.js' import type { SonarPaging } from './project.js' @@ -89,12 +89,12 @@ export const createUser: StepCall = async (payload) => { } } catch (error) { return { + error: parseError(error), status: { result: 'KO', // @ts-ignore prévoir une fonction générique message: error.message, }, - error: JSON.stringify(error), } } } @@ -136,11 +136,11 @@ export const deleteUser: StepCall = async (payload) => { } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: 'Failed', }, - error: JSON.stringify(error), } } } diff --git a/plugins/vault/src/functions.ts b/plugins/vault/src/functions.ts index f9c13a458..77908a96f 100644 --- a/plugins/vault/src/functions.ts +++ b/plugins/vault/src/functions.ts @@ -1,4 +1,4 @@ -import type { StepCall, ArchiveProjectExecArgs } from '@cpn-console/hooks' +import { type StepCall, type ArchiveProjectExecArgs, parseError } from '@cpn-console/hooks' export const archiveDsoProject: StepCall = async (payload) => { try { @@ -17,11 +17,11 @@ export const archiveDsoProject: StepCall = async (payloa } } catch (error) { return { + error: parseError(error), status: { result: 'KO', message: error instanceof Error ? error.message : 'An unexpected error has occurred', }, - error: JSON.stringify(error), } } } diff --git a/release-please-config.json b/release-please-config.json index 60bc091b3..040f84c2a 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -2,6 +2,7 @@ "packages": { ".": { "package-name": "console", + "include-component-in-tag": false, "extra-files": [ "helm/Chart.yaml", "helm/values.yaml"