Skip to content

871: Add update k8s verstion tag CI function#864

Open
angelayu0530 wants to merge 1 commit intomainfrom
871
Open

871: Add update k8s verstion tag CI function#864
angelayu0530 wants to merge 1 commit intomainfrom
871

Conversation

@angelayu0530
Copy link
Collaborator

@angelayu0530 angelayu0530 commented Mar 18, 2026

871

Description of changes

Updated k8s verison of tag ci function

Checklist before review

  • I have done a thorough self-review of the PR
  • Copilot has reviewed my latest changes, and all comments have been fixed and/or closed.
  • If I have made database changes, I have made sure I followed all the db repo rules listed in the wiki here. (check if no db changes)
  • All tests have passed
  • I have successfully deployed this PR to staging
  • I have done manual QA in both dev (and staging if possible) and attached screenshots below.

Screenshots

@angelayu0530 angelayu0530 force-pushed the 871 branch 2 times, most recently from ad0cc23 to 90bc0cc Compare March 18, 2026 16:33
@github-actions
Copy link
Contributor

Ticket Validation Failed

The following issues were found with the attached ticket:

  • Acceptance Criteria (AC) is not provided in the ticket description.

@github-actions
Copy link
Contributor

Title

871: Add update k8s verstion tag CI function


PR Type

Enhancement


Description

  • Automates Kubernetes image tag updates via GitHub PRs.

  • Introduces deploy-web composite action for deployments.

  • Refactors build-image action to output Docker tags.

  • Updates ci-cd.yml for staging and production deployments.


Diagram Walkthrough

flowchart LR
  A[ci-cd.yml] --> B{Deploy Web Job};
  B -- "Uses" --> C[deploy-web composite action];
  C -- "Builds & Pushes Image" --> D[build-image composite action];
  D -- "Outputs Tag" --> C;
  C -- "Updates K8s Manifest" --> E[deploy script];
  E -- "Creates & Merges PR" --> F[create-k8s-pr script];
  F -- "Updates" --> G[k8s-personal repo];
Loading

File Walkthrough

Relevant files
Enhancement
7 files
index.ts
Refactors image build script to use environment and output tag
+38/-19 
index.ts
Adds new script to trigger Kubernetes manifest updates     
+53/-0   
types.ts
Adds `Type` enum for service types                                             
+2/-0     
create-k8s-pr.ts
Implements GitHub API logic to update Kustomization files via PRs
+140/-0 
short-id.ts
Adds utility to generate short unique IDs                               
+10/-0   
action.yml
Refactors composite action for generic image building and tag output
+11/-46 
action.yaml
Adds new composite action for web service deployment         
+19/-0   
Dependencies
1 files
package.json
Adds `yaml` and `zod` dependencies for Kustomization parsing
+3/-1     
Configuration changes
1 files
ci-cd.yml
Updates CI/CD workflow to use new deployment actions and structure
+67/-25 

@github-actions
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 Security concerns

GitHub PAT Permissions:
The updateK8sTagWithPR function uses a GITHUB_PAT to interact with the k8s-personal repository, creating branches, committing files, and merging pull requests. It is crucial to ensure that the GITHUB_PAT used in the CI/CD pipeline has only the minimum necessary permissions (e.g., contents:write, pull_requests:write) for the k8s-personal repository, and not broader permissions across the organization or user account. This minimizes the potential impact in case the token is compromised.

⚡ Recommended focus areas for review

Test Coverage

The new utility function updateK8sTagWithPR interacts with external APIs (GitHub) and performs critical operations like parsing and modifying YAML files. While CI/CD scripts are often validated through pipeline execution, adding dedicated unit or integration tests for this function, potentially by mocking Octokit and YAML interactions, would significantly enhance its robustness and maintainability.

export async function updateK8sTagWithPR({
  githubPat,
  newTag,
  imageName,
  kustomizationFilePath,
  environment,
}: {
  githubPat: string;
  newTag: string;
  imageName: string;
  kustomizationFilePath: string;
  environment: Environment;
}) {
  const client = new Octokit({
    auth: githubPat,
  });

  const newBranchName = `${imageName}-${newTag}-${generateShortId()}`;

  const { data: repo } = await client.rest.repos.get({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
  });

  const baseBranch = repo.default_branch;

  const { data: ref } = await client.rest.git.getRef({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    ref: `heads/${baseBranch}`,
  });

  await client.rest.git.createRef({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    ref: `refs/heads/${newBranchName}`,
    sha: ref.object.sha,
  });

  const { data: file } = await client.rest.repos.getContent({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    path: kustomizationFilePath,
    ref: newBranchName,
  });

  if (Array.isArray(file)) throw new Error("Unexpected file shape found");
  if (!file) throw new Error("Kustomization file not found");
  if (file.type !== "file") throw new Error("Unexpected file type found");
  if (!file.content) throw new Error("Kustomization file is empty");

  const currentYaml = Buffer.from(file.content ?? "", "base64").toString();
  const doc = yaml.parseDocument(currentYaml);
  const yamlObj = kustomizeSchema.parse(doc.toJS());

  const targetImage = yamlObj.images?.find((img) => img.name === imageName);
  if (!targetImage) {
    console.debug(yamlObj);
    throw new Error("Target image could not be found.");
  }

  targetImage.newTag = newTag;
  doc.set("images", yamlObj.images);

  const updatedYaml = doc.toString();

  await client.rest.repos.createOrUpdateFileContents({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    path: kustomizationFilePath,
    message: `deploy: update ${imageName} to ${newTag}`,
    content: Buffer.from(updatedYaml).toString("base64"),
    sha: file.sha,
    branch: newBranchName,
  });

  const { data: pr } = await client.rest.pulls.create({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    title: `Deploying ${newTag} for ${imageName} in ${environment}`,
    head: newBranchName,
    base: baseBranch,
    body: `Automated image tag change to ${newTag} for ${imageName} in ${environment} triggered by [${ORIGIN}](https://github.com/${ORIGIN}).`,
  });

  await client.rest.pulls.merge({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    pull_number: pr.number,
    merge_method: "squash",
  });
}
Clarity of Options

The yargs option for dockerUpload is defined with both default: false and demandOption: true. While functionally valid (the option will always be present, either by user input or default), this combination can be slightly confusing as demandOption typically implies an explicit user-provided value. Consider if demandOption is strictly necessary when a default is provided.

.option("dockerUpload", {
  type: "boolean",
  default: false,
  demandOption: true,
})
Hardcoded Image Name

The imageName is hardcoded as docker.io/tahminator/codebloom. If the project expands to include multiple images or if the image name changes in the future, this hardcoded value would require a code modification. Consider making this configurable, perhaps through an environment variable or an additional yargs option, to improve flexibility.

imageName: "docker.io/tahminator/codebloom",

@angelayu0530 angelayu0530 force-pushed the 871 branch 2 times, most recently from 3d313e0 to 1e8d09f Compare March 18, 2026 17:48
@github-actions
Copy link
Contributor

Title

871: Add update k8s verstion tag CI function


PR Type

Enhancement


Description

  • Automates Kubernetes image tag updates via GitHub.

  • Introduces new deployment scripts and actions.

  • Refactors image build processes for modularity.

  • Updates CI/CD workflows for web and bot services.


Diagram Walkthrough

flowchart LR
  A[CI/CD Workflow] --> B{Build Image (Web/Bot)};
  B --> C[Get Image Tag];
  C --> D[Deploy Script];
  D --> E[Create K8s PR Utility];
  E -- "Updates kustomization.yaml" --> F[k8s-personal Repo];
  F -- "Creates & Merges PR" --> F;
Loading

File Walkthrough

Relevant files
Ci/cd enhancement
3 files
index.ts
Refactor image build script for environment and output     
+38/-19 
standup-bot.ts
Enable standup-bot image build to output tag                         
+22/-1   
index.ts
Add new script for deploying image tags to K8s                     
+70/-0   
Type definition
1 files
types.ts
Add new 'Type' definition for services                                     
+2/-0     
Ci/cd utility
1 files
create-k8s-pr.ts
Implement utility to update K8s tags via GitHub PRs           
+140/-0 
Utility
1 files
short-id.ts
Add utility function for generating short IDs                       
+10/-0   
Ci/cd refactoring
2 files
action.yml
Refactor build-image composite action inputs and outputs 
+11/-46 
action.yml
Add tag output to standup-bot build composite action         
+7/-1     
Ci/cd orchestration
2 files
action.yaml
Add new composite action for standup-bot deployment           
+27/-0   
action.yaml
Add new composite action for web service deployment           
+19/-0   
Dependencies
1 files
package.json
Add 'yaml' and 'zod' dependencies for script parsing         
+3/-1     
Ci/cd workflow update
1 files
ci-cd.yml
Update CI/CD workflow to use new deployment actions           
+68/-41 

@github-actions
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Error Handling

The kustomizeSchema.parse(doc.toJS()) call uses zod for schema validation. If the kustomization.yaml file does not conform to the expected schema, zod will throw a detailed error. Consider explicitly catching ZodError and logging its details (e.g., error.issues) to provide more actionable debugging information in the CI logs, rather than relying on a generic error catch.

const yamlObj = kustomizeSchema.parse(doc.toJS());

@angelayu0530
Copy link
Collaborator Author

/deploy

prettier

prettier fixes

Fix standup bot issue

changes

fix issues
@angelayu0530
Copy link
Collaborator Author

/deploy

@github-actions
Copy link
Contributor

Title

871: Add update k8s verstion tag CI function


PR Type

Enhancement


Description

  • Automates Kubernetes image tag updates via GitHub PRs.

  • Introduces new composite actions for web and standup bot deployments.

  • Refactors CI/CD workflows to use new deployment strategy.

  • Adds yaml and zod for K8s manifest parsing.


Diagram Walkthrough

flowchart LR
  A[Build Web Image] --> B{Deploy Web Service};
  B --> C[Update K8s Manifest (Web)];
  C --> D[Create & Merge K8s PR];

  E[Build Standup Bot Image] --> F{Deploy Standup Bot Service};
  F --> G[Update K8s Manifest (Standup Bot)];
  G --> D;
Loading

File Walkthrough

Relevant files
Enhancement
6 files
index.ts
Refactor image build script to use environment and output tag.
+38/-19 
standup-bot.ts
Update standup bot image build script to output tag.         
+22/-1   
index.ts
New script to trigger Kubernetes manifest updates for services.
+70/-0   
types.ts
Add `Type` enum for web and standup-bot services.               
+2/-0     
action.yaml
New composite action to build and deploy the standup bot.
+27/-0   
action.yaml
New composite action to build and deploy the web service.
+19/-0   
Utility
2 files
create-k8s-pr.ts
New utility to update K8s `kustomization.yaml` via GitHub PRs.
+140/-0 
short-id.ts
New utility to generate short unique IDs for branches.     
+10/-0   
Configuration changes
4 files
action.yml
Simplify web image build action, add environment input and tag output.
+11/-46 
action.yml
Add `tag` output to standup bot image build action.           
+7/-1     
ci-cd.yml
Update main CI/CD workflow to use new deployment composite actions.
+68/-41 
deploy-command.yml
Update deploy command workflow to use new build image action.
+32/-4   
Dependencies
1 files
package.json
Add `yaml` and `zod` dependencies for K8s manifest handling.
+3/-1     

@github-actions
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Insufficient Test Coverage

The new updateK8sTagWithPR function is a critical component for automated deployments. Consider adding dedicated unit or integration tests for this utility, possibly by mocking GitHub API interactions, to ensure its robustness and correctness under various scenarios.

export async function updateK8sTagWithPR({
  githubPat,
  newTag,
  imageName,
  kustomizationFilePath,
  environment,
}: {
  githubPat: string;
  newTag: string;
  imageName: string;
  kustomizationFilePath: string;
  environment: Environment;
}) {
  const client = new Octokit({
    auth: githubPat,
  });

  const newBranchName = `${imageName}-${newTag}-${generateShortId()}`;

  const { data: repo } = await client.rest.repos.get({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
  });

  const baseBranch = repo.default_branch;

  const { data: ref } = await client.rest.git.getRef({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    ref: `heads/${baseBranch}`,
  });

  await client.rest.git.createRef({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    ref: `refs/heads/${newBranchName}`,
    sha: ref.object.sha,
  });

  const { data: file } = await client.rest.repos.getContent({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    path: kustomizationFilePath,
    ref: newBranchName,
  });

  if (Array.isArray(file)) throw new Error("Unexpected file shape found");
  if (!file) throw new Error("Kustomization file not found");
  if (file.type !== "file") throw new Error("Unexpected file type found");
  if (!file.content) throw new Error("Kustomization file is empty");

  const currentYaml = Buffer.from(file.content ?? "", "base64").toString();
  const doc = yaml.parseDocument(currentYaml);
  const yamlObj = kustomizeSchema.parse(doc.toJS());

  const targetImage = yamlObj.images?.find((img) => img.name === imageName);
  if (!targetImage) {
    console.debug(yamlObj);
    throw new Error("Target image could not be found.");
  }

  targetImage.newTag = newTag;
  doc.set("images", yamlObj.images);

  const updatedYaml = doc.toString();

  await client.rest.repos.createOrUpdateFileContents({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    path: kustomizationFilePath,
    message: `deploy: update ${imageName} to ${newTag}`,
    content: Buffer.from(updatedYaml).toString("base64"),
    sha: file.sha,
    branch: newBranchName,
  });

  const { data: pr } = await client.rest.pulls.create({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    title: `Deploying ${newTag} for ${imageName} in ${environment}`,
    head: newBranchName,
    base: baseBranch,
    body: `Automated image tag change to ${newTag} for ${imageName} in ${environment} triggered by [${ORIGIN}](https://github.com/${ORIGIN}).`,
  });

  await client.rest.pulls.merge({
    owner: MANIFEST_REPO_OWNER,
    repo: MANIFEST_REPO,
    pull_number: pr.number,
    merge_method: "squash",
  });
}
Hardcoded Configuration

The repository details (MANIFEST_REPO, MANIFEST_REPO_OWNER, ORIGIN) are hardcoded. Consider making these configurable via script arguments or environment variables to improve flexibility and maintainability, especially if this script is intended for broader use or if these details might change in the future.

const MANIFEST_REPO = "k8s-personal";
const MANIFEST_REPO_OWNER = "tahminator";

// this should point to this repo name
const ORIGIN = "tahminator/codebloom";
Automated PR Merge

The pull request created to update the K8s tag is immediately merged. This design choice streamlines automated deployments but removes a human review step for changes to the K8s manifests. Ensure this behavior aligns with the desired deployment strategy and risk tolerance.

await client.rest.pulls.merge({
  owner: MANIFEST_REPO_OWNER,
  repo: MANIFEST_REPO,
  pull_number: pr.number,
  merge_method: "squash",
});

@angelayu0530
Copy link
Collaborator Author

/deploy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant