diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 39918794..1f54cc49 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -214,9 +214,16 @@ A Git profile contains: - **name** – unique identifier for the profile - **repository** – the GitHub repository in the format `owner/repo` -- **authenticationType** – either `SSH` or `PAT` (Personal Access Token) -- **username** – optional username (only for token authentication) -- **token** – Personal Access Token (if `PAT` is selected) +- **authenticationType** – either `SSH` or `HTTPS` +- **username** – optional username (used for commit authoring) + +#### Authentication +The authentication is delegated your local Git configuration, based on the selected method (SSH or HTTPS). + +Besides profile authentication, the git related commands can also be set up in pipelines via the following environment variables: +- GIT_REPOSITORY – the GitHub repository in the format `owner/repo` +- GIT_TOKEN – a GitHub personal access token with `repo` scope (for HTTPS authentication) +- GIT_USERNAME – username (used for commit authoring) #### When to create a Git profile A Git profile should be created to represent of Github repository and Github user credentials you want to use for interacting with content. diff --git a/docs/internal-architecture.md b/docs/internal-architecture.md index 5d08e570..94fbb999 100644 --- a/docs/internal-architecture.md +++ b/docs/internal-architecture.md @@ -73,14 +73,15 @@ A git profile includes: - **Name** – the profile identifier - **Repository** – the Github repository in the format `owner/repository` - **Authentication** – either: - - **Personal Access Token (PAT)** for HTTPS-based authentication - - **SSH key** for SSH-based authentication -- **Username** (optional, used with PATs) + - **HTTPS** uses HTTPS-based authentication + - **SSH** uses SSH-based authentication +- **Username** (optional, used for commit authoring) ### Implementation - Credentials are fetched from the active Git profile stored in a local directory -and translated into an authenticated Github repository URL. +and then based on the authentication method a Git URL is constructed, which delegates authentication +to the local Git configuration (SSH keys or credential helpers). - Git operations are executed via [`simple-git`](https://github.com/steveukx/git-js). - Temporary working directories are created when pulling/pushing, ensuring isolation and cleanup. diff --git a/src/commands/git-profile/git-profile-command.service.ts b/src/commands/git-profile/git-profile-command.service.ts index 670a3f37..04a0c9af 100644 --- a/src/commands/git-profile/git-profile-command.service.ts +++ b/src/commands/git-profile/git-profile-command.service.ts @@ -13,11 +13,10 @@ export class GitProfileCommandService { profile.name = await questions.ask("Name of the Git profile to create: "); profile.username = await questions.ask("Your Git username: "); profile.repository = await questions.ask("Your repository (format: repoOwner/repoName): "); - const type = await questions.ask("Authentication type: PAT (1), SSH token (2): " ); + const type = await questions.ask("Authentication type: HTTPS (1), SSH token (2): " ); switch (type) { case "1": - profile.authenticationType = AuthenticationType.PAT; - profile.token = await questions.ask("Your Personal Access Token (PAT): " ); + profile.authenticationType = AuthenticationType.HTTPS; break; case "2": profile.authenticationType = AuthenticationType.SSH; diff --git a/src/content-cli.ts b/src/content-cli.ts index 90ee7114..60cb6305 100644 --- a/src/content-cli.ts +++ b/src/content-cli.ts @@ -33,6 +33,7 @@ program.configureHelp({ program.version(VersionUtils.getCurrentCliVersion()); program.option("-q, --quietmode", "Reduce output to a minimum", false); program.option("-p, --profile [profile]"); +program.option("--gitProfile [gitProfile]", "Git profile to use"); program.option("--debug", "Print debug messages", false); program.option("--dev", "Development Mode", false); program.parseOptions(process.argv); diff --git a/src/core/git-profile/git-profile.interface.ts b/src/core/git-profile/git-profile.interface.ts index b6aa1e81..4c6e7d35 100644 --- a/src/core/git-profile/git-profile.interface.ts +++ b/src/core/git-profile/git-profile.interface.ts @@ -1,15 +1,15 @@ export interface GitProfile { name: string; username?: string; - repository: string; // in the format "owner/repository" token?: string; + repository: string; // in the format "owner/repository" authenticationType: AuthenticationType; } -export type AuthenticationType = "SSH" | "Personal Access Token"; +export type AuthenticationType = "SSH" | "HTTPS"; // tslint:disable-next-line:variable-name export const AuthenticationType: { [key: string]: AuthenticationType } = { SSH: "SSH", - PAT: "Personal Access Token", + HTTPS: "HTTPS", }; diff --git a/src/core/git-profile/git-profile.service.ts b/src/core/git-profile/git-profile.service.ts index f563e132..ce109455 100644 --- a/src/core/git-profile/git-profile.service.ts +++ b/src/core/git-profile/git-profile.service.ts @@ -16,7 +16,7 @@ export class GitProfileService { public async findProfile(profileName: string): Promise { return new Promise((resolve, reject) => { - if (process.env.USERNAME && process.env.GIT_TOKEN && process.env.REPOSITORY) { + if (process.env.GIT_USERNAME && process.env.GIT_TOKEN && process.env.GIT_REPOSITORY) { resolve(this.buildProfileFromEnvVariables()); } else { if (!profileName) { @@ -42,7 +42,7 @@ export class GitProfileService { resolve(profile); }) .catch(err => { - logger.error(new FatalError("Git Profile does not exit.")); + logger.error(new FatalError("Git Profile does not exist.")); reject(err); }); }); @@ -68,10 +68,11 @@ export class GitProfileService { private async buildProfileFromEnvVariables(): Promise { const profileVariables = this.getProfileEnvVariables(); const profile: GitProfile = { - name: profileVariables.repository, + name: "env-profile", + username: profileVariables.username, repository: profileVariables.repository, - token: profileVariables.apiToken, - authenticationType: AuthenticationType.SSH, + token: profileVariables.token, + authenticationType: AuthenticationType.HTTPS, }; profile.authenticationType = await GitProfileValidator.validateProfile(profile); return profile; @@ -112,9 +113,9 @@ export class GitProfileService { private getProfileEnvVariables(): any { return { - username: process.env.USERNAME, + username: process.env.GIT_USERNAME, token: process.env.GIT_TOKEN, - repository: process.env.REPOSITORY, + repository: process.env.GIT_REPOSITORY, }; } } diff --git a/src/core/git-profile/git-profile.validator.ts b/src/core/git-profile/git-profile.validator.ts index 054e862a..f3907333 100644 --- a/src/core/git-profile/git-profile.validator.ts +++ b/src/core/git-profile/git-profile.validator.ts @@ -15,8 +15,5 @@ export class GitProfileValidator { if (gitProfile.authenticationType == null) { logger.error(new FatalError("The authentication type can not be empty")); } - if (gitProfile.authenticationType === AuthenticationType.PAT && gitProfile.token == null) { - logger.error(new FatalError("The token can not be empty for authentication with PAT")); - } } } diff --git a/src/core/git-profile/git/git.service.ts b/src/core/git-profile/git/git.service.ts index acd18fcc..1f9b6a7c 100644 --- a/src/core/git-profile/git/git.service.ts +++ b/src/core/git-profile/git/git.service.ts @@ -104,9 +104,12 @@ export class GitService { return `git@github.com:${repository}.git`; } - const safeUsername = encodeURIComponent(username ?? "git"); - const safeToken = encodeURIComponent(token); - return `https://${safeUsername}:${safeToken}@github.com/${repository}.git`; + if (token) { + const safeUsername = encodeURIComponent(username ?? "git"); + const safeToken = encodeURIComponent(token); + return `https://${safeUsername}:${safeToken}@github.com/${repository}.git`; + } + return `https://github.com/${repository}.git`; } private cleanupGitDirectory(repoDirectory: string): void { diff --git a/tests/commands/deployment/deployment-list-active.spec.ts b/tests/commands/deployment/deployment-list-active.spec.ts index 838bf2fb..40b66d05 100644 --- a/tests/commands/deployment/deployment-list-active.spec.ts +++ b/tests/commands/deployment/deployment-list-active.spec.ts @@ -13,9 +13,9 @@ describe("Deployment list active", () => { packageVersion: "1.0.0", deployableType: "app-package", status: DeploymentStatus.SUCCESS, - createdAt: new Date().getDate() + "", + createdAt: new Date().toISOString(), createdBy: "user-id", - deployedAt: new Date().getDate() + "", + deployedAt: new Date().toISOString(), statusMessage: "Deployment in progress", target: { id: "target-id", @@ -30,6 +30,7 @@ describe("Deployment list active", () => { await new DeploymentService(testContext).getActiveDeploymentForTarget(targetId, false); expect(loggingTestTransport.logMessages.length).toBe(1); + expect(loggingTestTransport.logMessages[0].message).toContain(`ID: ${deployment.id}, Package: ${deployment.packageKey}, Version: ${deployment.packageVersion}, Status: ${deployment.status}, Deployed at: ${new Date(deployment.deployedAt).toISOString()}`); });