Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions .github/scripts/get_new_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""
Description:
- Print the calculated new version.

Usage:
- python3 update_version.py --current-version <version> (--stage)

Version Scheme:
- <year>.<month>.<release count>(a<prerelease count>)

example:
- case 1:
- given:
- current version: 2025.1.1
- today : YEAR = 2025, MONTH = 1
- then:
- if stage is false:
- new version: 2025.1.2
- if stage is true:
- new version: 2025.1.2a0
- case 2:
- given:
- current version: 2025.1.2a0
- today : YEAR = 2025, MONTH = 1
- then:
- if stage is false:
- new version: 2025.1.2
- if stage is true:
- new version: 2025.1.2a1
- case 3:
- given:
- current version: 2025.1.1
- today : YEAR = 2025, MONTH = 2
- then:
- if stage is false:
- new version: 2025.2.0
- if stage is true:
- new version: 2025.2.0a0
"""

import argparse
import datetime
import typing

import packaging.version

PreType = tuple[typing.Literal["a", "b", "rc"], int] | None


class ArgumentNamespace(argparse.Namespace):
current: str
stage: bool = False


def increment_version_count(version: packaging.version.Version, is_stage: bool) -> str:
if (current_pre := version.pre) and current_pre[0] != "a":
raise ValueError(f"Unsupported pre-release version: {current_pre[0]}")

# Get the current date
today: datetime.date = datetime.date.today()

# Calculate the new version
new_count: int = 0
if version.major == today.year and version.minor == today.month:
if current_pre:
# If the current version is a pre-release, do not increment the count
new_count = version.micro
else:
# Same month, increment the count
new_count = version.micro + 1
else:
# Different month, reset the count
new_count = 1
current_pre = None

new_pre: PreType = ((current_pre[0], current_pre[1] + 1) if current_pre else ("a", 0)) if is_stage else None
new_pre_str = f"{new_pre[0]}{new_pre[1]}" if new_pre else ""
return f"{today.year}.{today.month}.{new_count}{new_pre_str}"


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Update version in files.")
parser.add_argument("--current", type=str, required=True)
parser.add_argument("--stage", default=False, action="store_true")

args = parser.parse_args(namespace=ArgumentNamespace())
print(increment_version_count(packaging.version.parse(args.current), args.stage))
103 changes: 103 additions & 0 deletions .github/scripts/update_ssm_parameter_store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import argparse
import json
import pathlib
import typing

import boto3

if typing.TYPE_CHECKING:
import mypy_boto3_ssm

ssm_client: "mypy_boto3_ssm.SSMClient" = boto3.client("ssm")


class ValueDiff(typing.NamedTuple):
old: str | None
new: str | None


class ParameterDiffCollection(typing.NamedTuple):
updated: dict[str, ValueDiff]
created: dict[str, ValueDiff]
deleted: dict[str, ValueDiff]


def read_json_file(json_file: pathlib.Path, stage: str) -> dict[str, str]:
if not (data_groups := json.loads(json_file.read_text())) or not isinstance(data_groups, dict):
raise ValueError("JSON 파일이 잘못되었습니다.")

if not (data := typing.cast(dict[str, typing.Any], data_groups.get(stage))):
raise ValueError("JSON 파일에 해당 스테이지의 파라미터가 없습니다.")

if not all(isinstance(k, str) and isinstance(v, (str, int, float, bool)) for k, v in data.items()):
# object / array / null is not allowed here
raise ValueError("JSON 파일의 파라미터가 잘못되었습니다.")

return {k: str(v) for k, v in data.items()}


def read_parameter_store(project_name: str, stage: str) -> dict[str, str]:
parameters: dict[str, str] = {}
next_token = "" # nosec: B105
while next_token is not None:
result = ssm_client.get_parameters_by_path(
Path=f"/{project_name}/{stage}",
MaxResults=10,
**({"NextToken": next_token} if next_token else {}),
)
parameters.update({p["Name"].split("/")[-1]: p["Value"] for p in result["Parameters"]})
next_token = result.get("NextToken")
return parameters


def get_parameter_diff(old_parameters: dict[str, str], new_parameters: dict[str, str]) -> ParameterDiffCollection:
created, updated, deleted = {}, {}, {}

for fields in old_parameters.keys() | new_parameters.keys():
value = ValueDiff(old=old_parameters.get(fields), new=new_parameters.get(fields))
if value.old != value.new:
if value.old is None:
created[fields] = value
elif value.new is None:
deleted[fields] = value
else:
updated[fields] = value

return ParameterDiffCollection(updated=updated, created=created, deleted=deleted)


def update_parameter_store(project_name: str, stage: str, diff: ParameterDiffCollection) -> None:
for field, values in {**diff.created, **diff.updated}.items():
ssm_client.put_parameter(
Name=f"/{project_name}/{stage}/{field}",
Value=values.new,
Type="String",
Overwrite=True,
)

if diff.deleted:
ssm_client.delete_parameters(Names=[f"/{project_name}/{stage}/{field}" for field in diff.deleted.keys()])


def main(project_name: str, stage: str, json_file: pathlib.Path) -> None:
if not all([json_file.is_file(), project_name, stage]):
raise ValueError("인자를 확인해주세요.")

old_params = read_parameter_store(project_name, stage)
new_params = read_json_file(json_file, stage)
diff = get_parameter_diff(old_params, new_params)

print(f"Updated: '{', '.join(diff.updated.keys())}'")
print(f"Created: '{', '.join(diff.created.keys())}'")
print(f"Deleted: '{', '.join(diff.deleted.keys())}'")
update_parameter_store(project_name, stage, diff)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--project_name", type=str)
parser.add_argument("--stage", type=str)
parser.add_argument("--json_file", type=pathlib.Path)

args = parser.parse_args()
main(project_name=args.project_name, stage=args.stage, json_file=args.json_file)
36 changes: 36 additions & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Check lint

on:
pull_request:
push:
branches:
- 'main'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true

jobs:
lint:
name: Run lint
runs-on: ubuntu-latest
steps:
- name: Checkout source codes
uses: actions/checkout@v4

- uses: actions/setup-python@v4
with:
python-version: '3.12'

- name: Install dependencies
run: pip install 'pre-commit'

- name: cache pre-commit repo
uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}

- name: Run pre-commit
id: run-pre-commit
run: pre-commit run --all-files --show-diff-on-failure
Loading