diff --git a/git-push-service/src/application.ts b/git-push-service/src/application.ts deleted file mode 100644 index 653d1ea10..000000000 --- a/git-push-service/src/application.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as yaml from 'js-yaml' - -export type Application = { - name: string - project: string - source: { - repository: string - branch: string - path: string - } - destination: { - namespace: string - } - annotations: string[] -} - -type KubernetesApplication = { - apiVersion: string - kind: string - metadata: { - name: string - namespace: string - annotations?: { [key: string]: string } - finalizers: string[] - } - spec: { - project: string - source: { - repoURL: string - targetRevision: string - path: string - } - destination: { - server: string - namespace: string - } - syncPolicy: { - automated: { - prune: boolean - } - } - } -} - -export const generateApplicationManifest = (a: Application): string => { - const application: KubernetesApplication = { - apiVersion: 'argoproj.io/v1alpha1', - kind: 'Application', - metadata: { - name: a.name, - namespace: 'argocd', - finalizers: ['resources-finalizer.argocd.argoproj.io'], - }, - spec: { - project: a.project, - source: { - repoURL: `https://github.com/${a.source.repository}.git`, - targetRevision: a.source.branch, - path: a.source.path, - }, - destination: { - server: `https://kubernetes.default.svc`, - namespace: a.destination.namespace, - }, - syncPolicy: { - automated: { - prune: true, - }, - }, - }, - } - if (a.annotations.length > 0) { - const annotations: { [_: string]: string } = {} - for (const s of a.annotations) { - const k = s.substring(0, s.indexOf('=')) - const v = s.substring(s.indexOf('=') + 1) - annotations[k] = v - } - application.metadata.annotations = annotations - } - return yaml.dump(application) -} diff --git a/git-push-service/src/arrange.ts b/git-push-service/src/arrange.ts index d70b42095..9abe3fd9c 100644 --- a/git-push-service/src/arrange.ts +++ b/git-push-service/src/arrange.ts @@ -1,8 +1,8 @@ -import { promises as fs } from 'fs' import * as core from '@actions/core' +import * as fs from 'fs/promises' import * as io from '@actions/io' import * as path from 'path' -import { Application, generateApplicationManifest } from './application.js' +import * as yaml from 'js-yaml' type Inputs = { workspace: string @@ -13,48 +13,71 @@ type Inputs = { branch: string applicationAnnotations: string[] destinationRepository: string + currentRef: string + currentSha: string } -export const arrangeManifests = async (inputs: Inputs): Promise => { - return await arrangeServiceManifests(inputs) +export const writeManifests = async (inputs: Inputs): Promise => { + await writeServiceManifest(inputs.manifests, `${inputs.workspace}/services/${inputs.service}/generated.yaml`) + await writeApplicationManifest(inputs) } -const arrangeServiceManifests = async (inputs: Inputs): Promise => { - core.info(`arrange the manifests of the service`) - await concatServiceManifests(inputs.manifests, `${inputs.workspace}/services/${inputs.service}/generated.yaml`) +const writeServiceManifest = async (sourcePaths: string[], destinationPath: string) => { + const sourceContents = await Promise.all( + sourcePaths.map(async (manifestPath) => await fs.readFile(manifestPath, 'utf-8')), + ) + const concatManifest = sourceContents.join('\n---\n') + core.info(`Writing the service manifest to ${destinationPath}`) + await io.mkdirP(path.dirname(destinationPath)) + await fs.writeFile(destinationPath, concatManifest) +} - await putApplicationManifest( - { +const writeApplicationManifest = async (inputs: Inputs) => { + const application = { + apiVersion: 'argoproj.io/v1alpha1', + kind: 'Application', + metadata: { name: `${inputs.namespace}--${inputs.service}`, + namespace: 'argocd', + finalizers: ['resources-finalizer.argocd.argoproj.io'], + annotations: { + ...parseApplicationAnnotations(inputs.applicationAnnotations), + 'github.ref': inputs.currentRef, + 'github.sha': inputs.currentSha, + 'github.action': 'git-push-service', + }, + }, + spec: { project: inputs.project, source: { - repository: inputs.destinationRepository, - branch: inputs.branch, + repoURL: `https://github.com/${inputs.destinationRepository}.git`, + targetRevision: inputs.branch, path: `services/${inputs.service}`, }, destination: { + server: `https://kubernetes.default.svc`, namespace: inputs.namespace, }, - annotations: inputs.applicationAnnotations, + syncPolicy: { + automated: { + prune: true, + }, + }, }, - inputs.workspace, - ) -} + } -const concatServiceManifests = async (manifestPaths: string[], destinationPath: string) => { - const manifestContents = await Promise.all( - manifestPaths.map(async (manifestPath) => (await fs.readFile(manifestPath)).toString()), - ) - const concatManifest = manifestContents.join('\n---\n') - core.info(`writing to ${destinationPath}`) - await io.mkdirP(path.dirname(destinationPath)) - await fs.writeFile(destinationPath, concatManifest) + await io.mkdirP(`${inputs.workspace}/applications`) + const destination = `${inputs.workspace}/applications/${application.metadata.name}.yaml` + core.info(`Writing the application manifest to ${destination}`) + await fs.writeFile(destination, yaml.dump(application)) } -const putApplicationManifest = async (application: Application, workspace: string) => { - await io.mkdirP(`${workspace}/applications`) - const destination = `${workspace}/applications/${application.name}.yaml` - core.info(`writing to ${destination}`) - const content = generateApplicationManifest(application) - await fs.writeFile(destination, content) +const parseApplicationAnnotations = (applicationAnnotations: string[]): Record => { + const r: Record = {} + for (const s of applicationAnnotations) { + const k = s.substring(0, s.indexOf('=')) + const v = s.substring(s.indexOf('=') + 1) + r[k] = v + } + return r } diff --git a/git-push-service/src/run.ts b/git-push-service/src/run.ts index 55af5477c..1d7db0b88 100644 --- a/git-push-service/src/run.ts +++ b/git-push-service/src/run.ts @@ -5,7 +5,7 @@ import * as core from '@actions/core' import * as github from '@actions/github' import * as git from './git.js' import * as glob from '@actions/glob' -import { arrangeManifests } from './arrange.js' +import { writeManifests } from './arrange.js' import { retry } from './retry.js' import { updateBranchByPullRequest } from './pull.js' @@ -54,7 +54,7 @@ export const run = async (inputs: Inputs): Promise => { const push = async (manifests: string[], inputs: Inputs): Promise => { const workspace = await fs.mkdtemp(path.join(os.tmpdir(), 'git-push-service-action-')) - core.info(`created workspace at ${workspace}`) + core.info(`Created a workspace at ${workspace}`) const [owner, repo] = inputs.destinationRepository.split('/') const project = github.context.repo.repo @@ -63,42 +63,37 @@ const push = async (manifests: string[], inputs: Inputs): Promise 0 core.endGroup() - const applicationAnnotations = [ - ...inputs.applicationAnnotations, - `github.ref=${github.context.ref}`, - `github.sha=${github.context.sha}`, - `github.action=git-push-service`, - ] - - core.startGroup(`arrange manifests into workspace ${workspace}`) - await arrangeManifests({ + core.startGroup(`Writing the manifests into workspace ${workspace}`) + await writeManifests({ workspace, manifests, service: inputs.service, namespace: inputs.namespace, project, branch, - applicationAnnotations, + applicationAnnotations: inputs.applicationAnnotations, destinationRepository: inputs.destinationRepository, + currentRef: github.context.ref, + currentSha: github.context.sha, }) core.endGroup() const status = await git.status(workspace) if (status === '') { - core.info('nothing to commit') + core.info('Nothing to commit') return {} } const message = `Deploy ${project}/${inputs.namespace}/${inputs.service}\n\n${commitMessageFooter}` core.summary.addHeading(`Deploy ${project}/${inputs.namespace}/${inputs.service}`) - await core.group(`create a commit`, () => git.commit(workspace, message)) + await core.group(`Creating a commit`, () => git.commit(workspace, message)) if (!inputs.updateViaPullRequest) { - const code = await core.group(`push branch ${branch}`, () => git.pushByFastForward(workspace, branch)) + const code = await core.group(`Pushing the branch ${branch}`, () => git.pushByFastForward(workspace, branch)) if (code > 0) { return new Error(`failed to push branch ${branch} by fast-forward`) } @@ -108,7 +103,7 @@ const push = async (manifests: string[], inputs: Inputs): Promise git.pushByFastForward(workspace, branch)) + const code = await core.group(`Pushing a new branch ${branch}`, () => git.pushByFastForward(workspace, branch)) if (code > 0) { return new Error(`failed to push a new branch ${branch} by fast-forward`) } @@ -117,7 +112,7 @@ const push = async (manifests: string[], inputs: Inputs): Promise (await fs.readFile(f)).toString() +const readContent = async (f: string) => await fs.readFile(f, 'utf-8') -test('arrange a service manifest', async () => { +it('writes the service and application manifests', async () => { const workspace = await fs.mkdtemp(path.join(os.tmpdir(), 'git-push-action-')) - await arrangeManifests({ + await writeManifests({ workspace, manifests: [path.join(__dirname, `fixtures/a/generated.yaml`)], branch: `ns/project/overlay/namespace`, namespace: 'namespace', service: 'a', project: 'project', - applicationAnnotations: ['github.ref=refs/heads/main'], + applicationAnnotations: ['example=foo'], destinationRepository: 'octocat/manifests', + currentRef: 'refs/heads/main', + currentSha: '1234567890abcdef', }) expect(await readContent(path.join(workspace, `applications/namespace--a.yaml`))).toBe(applicationA) @@ -25,18 +27,20 @@ test('arrange a service manifest', async () => { ) }) -it('should concatenate service manifests if multiple are given', async () => { +it('concatenates the service manifests if multiple are given', async () => { const workspace = await fs.mkdtemp(path.join(os.tmpdir(), 'git-push-action-')) - await arrangeManifests({ + await writeManifests({ workspace, manifests: [path.join(__dirname, `fixtures/a/generated.yaml`), path.join(__dirname, `fixtures/b/generated.yaml`)], branch: `ns/project/overlay/namespace`, namespace: 'namespace', service: 'service', project: 'project', - applicationAnnotations: ['github.ref=refs/heads/main'], + applicationAnnotations: ['example=foo'], destinationRepository: 'octocat/manifests', + currentRef: 'refs/heads/main', + currentSha: '1234567890abcdef', }) expect(await readContent(path.join(workspace, `services/service/generated.yaml`))).toBe(`\ @@ -45,7 +49,7 @@ ${await readContent(path.join(__dirname, `fixtures/a/generated.yaml`))} ${await readContent(path.join(__dirname, `fixtures/b/generated.yaml`))}`) }) -test('overwrite even if a file exists', async () => { +it('overwrites if a file exists', async () => { const workspace = await fs.mkdtemp(path.join(os.tmpdir(), 'git-push-action-')) // put dummy files @@ -55,15 +59,17 @@ test('overwrite even if a file exists', async () => { await fs.mkdir(path.join(workspace, `services/a`)) await fs.writeFile(path.join(workspace, `services/a/generated.yaml`), 'fixture-generated-manifest') - await arrangeManifests({ + await writeManifests({ workspace, manifests: [path.join(__dirname, `fixtures/a/generated.yaml`)], branch: `ns/project/overlay/namespace`, namespace: 'namespace', service: 'a', project: 'project', - applicationAnnotations: ['github.ref=refs/heads/main'], + applicationAnnotations: ['example=foo'], destinationRepository: 'octocat/manifests', + currentRef: 'refs/heads/main', + currentSha: '1234567890abcdef', }) expect(await readContent(path.join(workspace, `applications/namespace--a.yaml`))).toBe(applicationA) @@ -81,7 +87,10 @@ metadata: finalizers: - resources-finalizer.argocd.argoproj.io annotations: + example: foo github.ref: refs/heads/main + github.sha: 1234567890abcdef + github.action: git-push-service spec: project: project source: