diff --git a/.github/helpers/gh_changelog_generator/gh_changelog_generator.py b/.github/helpers/gh_changelog_generator/gh_changelog_generator.py
index 6ebfa4a07eb..2c7eae79360 100644
--- a/.github/helpers/gh_changelog_generator/gh_changelog_generator.py
+++ b/.github/helpers/gh_changelog_generator/gh_changelog_generator.py
@@ -18,20 +18,15 @@
JIRA_PROJECT = os.getenv("JIRA_PROJECT", "DEV").strip('\"')
JIRA_RN_FIELD = os.getenv("JIRA_RN_FIELD", "customfield_10064").strip('\"')
-AHA_SERVER = os.getenv("AHA_SERVER", "https://labelstudio.aha.io").strip('\"')
-AHA_TOKEN = os.getenv("AHA_TOKEN").strip('\"')
-AHA_PRODUCT = os.getenv("AHA_PRODUCT", "LSDV").strip('\"')
-AHA_RN_FIELD = os.getenv("AHA_RN_FIELD", "release_notes").strip('\"')
-AHA_FETCH_STRATEGY = os.getenv("AHA_FETCH_STRATEGY", "PARKING_LOT").strip('\"') # PARKING_LOT or TAG
-AHA_TAG = os.getenv("AHA_TAG", "").strip('\"')
-AHA_ADDITIONAL_RELEASES_TAG = os.getenv("AHA_ADDITIONAL_RELEASES_TAG", "").strip('\"')
-
GH_REPO = os.getenv("GH_REPO", "").strip('\"')
GH_TOKEN = os.getenv("GH_TOKEN").strip('\"') # https://github.com/settings/tokens/new
LAUNCHDARKLY_SDK_KEY = os.getenv("LAUNCHDARKLY_SDK_KEY", '').strip('\"')
LAUNCHDARKLY_ENVIRONMENT = os.getenv("LAUNCHDARKLY_ENVIRONMENT", '').strip('\"')
+HELM_CHART_REPO = os.getenv("HELM_CHART_REPO", None)
+HELM_CHART_PATH = os.getenv("HELM_CHART_PATH", None)
+
OUTPUT_FILE_MD = os.getenv("OUTPUT_FILE_MD", 'output.md')
OUTPUT_FILE_JSON = os.getenv("OUTPUT_FILE_JSON", 'output.json')
@@ -57,117 +52,39 @@
DEFAULT_LABEL,
]
-
-class AHA:
- def __init__(self, server: str, token: str):
- self.server = server
- self.token = token
-
- def query(self, url: str, data: dict = None, params: dict = None, method: str = 'GET'):
- response = requests.request(
- method=method,
- url=f"{self.server}/{url}",
- headers={
- "Authorization": f"Bearer {self.token}",
- "Content-Type": "application/json",
- "Accept": "application/json",
- },
- json=data,
- params=params,
- )
- return response.json()
-
- def paginate(self, url: str, key: str, data: dict = None, method: str = 'GET', page: int = 0, per_page: int = 100):
- result = []
- current_page = page
- total_pages = None
- while total_pages is None or current_page <= total_pages:
- response_json = self.query(
- url=url,
- data=data,
- params={"page": current_page + 1, "per_page": per_page},
- method=method,
- )
- pagination = response_json.get('pagination', [])
- current_page = int(pagination.get('current_page'))
- total_pages = int(pagination.get('total_pages'))
- entries = response_json.get(key, [])
- result.extend(entries)
- return result
-
-
github_client = Github(GH_TOKEN)
github_repo = github_client.get_repo(GH_REPO)
jira_client = JIRA(JIRA_SERVER, basic_auth=(JIRA_USERNAME, JIRA_TOKEN))
-aha_client = AHA(AHA_SERVER, AHA_TOKEN)
+FEATURE_FLAGS = {}
-class AhaFeature:
- pr = None
-
- def __init__(self, feature_num: str, pr: int = None):
- self.type = "Aha! Feature"
- self.pr = pr
- feature = aha_client.query(f'api/v1/features/{feature_num}').get('feature')
- self.key = str(feature.get('reference_num'))
- self.status = str(feature.get('workflow_status').get('name'))
- self.label = feature.get('workflow_kind', {}).get('name', DEFAULT_LABEL)
- self.summary = str(feature.get('name'))
- self.release_note = next(
- (f.get('value') for f in feature.get('custom_fields', []) if f.get('key') == AHA_RN_FIELD), None)
- self.desc = self.release_note if self.release_note else self.summary
- self.link = str(feature.get('url'))
- self.releases_tags = next(
- (f.get('value') for f in feature.get('custom_fields', []) if f.get('key') == 'releases'), [])
-
- def set_releases_tags(self, tags: list[str]):
- aha_client.query(
- url=f'api/v1/features/{self.key}',
- data={"feature": {"custom_fields": {"releases": tags}}},
- method='PUT',
- )
- def __str__(self):
- return f"<{self.link}|[{self.key}]>: {self.desc} -- *{self.status}*"
+def ff_is_on(ff: dict) -> bool:
+ return ff.get('fallthrough').get('variation') == 0 if ff.get('on') else ff.get('offVariation') == 0
- def __key(self):
- return self.key
- def __dict__(self):
- return {
- "desc": self.desc,
- "link": self.link,
- "key": self.key,
- "status": self.status,
- "pr": self.pr
- }
+def ff_status(ff: dict) -> str:
+ is_on = ff_is_on(ff)
+ is_on_text = "On" if is_on else "Off"
+ options = []
+ if ff.get('rules'):
+ options.append('rules')
+ if ff.get('targets'):
+ options.append('targets')
+ if options:
+ options_text = ' and '.join(options)
+ return f'{is_on_text} with {options_text}'
+ return is_on_text
-class AhaRequirement(AhaFeature):
- def __init__(self, feature_num: str, pr: int = None):
- self.type = "Aha! Requirement"
- self.pr = pr
- feature = aha_client.query(f'api/v1/requirements/{feature_num}').get('requirement')
- self.key = str(feature.get('reference_num'))
- self.status = str(feature.get('workflow_status').get('name'))
- self.label = feature.get('workflow_kind', {}).get('name', DEFAULT_LABEL)
- self.summary = str(feature.get('name'))
- self.release_note = next(
- (f.get('value') for f in feature.get('custom_fields', []) if f.get('key') == AHA_RN_FIELD), None)
- self.desc = self.release_note if self.release_note else self.summary
- self.link = str(feature.get('url'))
- self.releases_tags = next(
- (f.get('value') for f in feature.get('custom_fields', []) if f.get('key') == 'releases'), [])
+def ff_link(ff: dict) -> str:
+ key = ff.get('key')
+ return f'https://app.launchdarkly.com/default/{LAUNCHDARKLY_ENVIRONMENT}/features/{key}/targeting'
- def set_releases_tags(self, tags: list[str]):
- aha_client.query(
- url=f'api/v1/requirements/{self.key}',
- data={"feature": {"custom_fields": {"releases": tags}}},
- method='PUT',
- )
+class JiraIssue:
+ pr = None
-class JiraIssue(AhaFeature):
def __init__(self, issue_number: str, pr: int = None):
self.type = "Jira Issue"
self.pr = pr
@@ -180,15 +97,51 @@ def __init__(self, issue_number: str, pr: int = None):
self.desc = self.release_note if self.release_note else self.summary
self.link = f"{JIRA_SERVER}/browse/{self.key}"
self.releases_tags = []
+ self.ffs = self.get_ffs()
def set_releases_tags(self, tags: list[str]):
pass
+ def __str__(self):
+ return f"<{self.link}|[{self.key}]>: {self.desc} -- *{self.status}*"
+
+ def __key(self):
+ return self.key
+
+ def __dict__(self):
+ return {
+ "desc": self.desc,
+ "link": self.link,
+ "key": self.key,
+ "status": self.status,
+ "pr": self.pr,
+ "ffs": self.ffs,
+ }
+
+ def get_ffs(self):
+ ff_key = self.key.lower().replace('-', '_')
+ result = []
+ for name, ff in FEATURE_FLAGS.items():
+ if ff_key in name:
+ key = ff.get('key')
+ on = ff_is_on(ff)
+ link = ff_link(ff)
+ status = ff_status(ff)
+ result.append(
+ {
+ 'key': key,
+ 'on': on,
+ 'link': link,
+ 'status': status,
+ }
+ )
+ return result
+
TASK_CACHE = {}
-def get_task(task_number: str, pr: int = None) -> AhaFeature or None:
+def get_task(task_number: str, pr: int = None) -> JiraIssue or None:
if task_number in TASK_CACHE.keys():
return TASK_CACHE.get(task_number)
try:
@@ -196,46 +149,10 @@ def get_task(task_number: str, pr: int = None) -> AhaFeature or None:
TASK_CACHE[task_number] = task
return task
except Exception as e:
- print(f'Could not find Issue {task_number} in Jira: {e}')
- try:
- task = AhaFeature(task_number, pr)
- TASK_CACHE[task_number] = task
- return task
- except Exception as e:
- print(f'Could not find Feature {task_number} in Aha!: {e}')
- try:
- task = AhaRequirement(task_number, pr)
- TASK_CACHE[task_number] = task
- return task
- except Exception as e:
- print(f'Could not find Requirement {task_number} in Aha!: {e}')
+ print(f'Could not find Issue {task_number} in Jira: {e}')
return None
-def get_aha_release(product: str, version: str):
- aha_releases = aha_client.query(f'api/v1/products/{product}/releases').get('releases', [])
- aha_sorted_releases = sorted(aha_releases, key=lambda x: x.get('name'), reverse=True)
- return next((e for e in aha_sorted_releases if version in e.get('name')), None)
-
-
-def get_aha_release_features(release_num: str) -> list[AhaFeature]:
- features = aha_client.query(f'api/v1/releases/{release_num}/features').get('features', [])
- tasks = set()
- for feature in features:
- if task := get_task(feature.get('reference_num')):
- tasks.add(task)
- return list(tasks)
-
-
-def get_aha_release_features_by_tag(tag: str) -> list[AhaFeature]:
- features = aha_client.paginate('api/v1/features', 'features', data={"tag": tag})
- tasks = set()
- for feature in features:
- if task := get_task(feature.get('reference_num')):
- tasks.add(task)
- return list(tasks)
-
-
def get_jira_release(project: str, version: str):
jira_project_versions = jira_client.project_versions(project=project)
jira_sorted_project_versions = sorted(jira_project_versions, key=lambda x: x.name, reverse=True)
@@ -256,7 +173,7 @@ def get_github_release(previous_ref: str, current_ref: str):
return github_repo.compare(previous_ref, current_ref)
-def get_github_release_tasks(commits) -> list[AhaFeature]:
+def get_github_release_tasks(commits) -> list[JiraIssue]:
tasks = set()
for commit in commits:
message_first_line = commit.commit.message.split("\n")[0]
@@ -268,16 +185,13 @@ def get_github_release_tasks(commits) -> list[AhaFeature]:
try:
pr = int(match.group(5))
except Exception as e:
- print(f'Could no parse pr from "{message_first_line}": {str(e)}')
+ print(f'Could not parse pr from "{message_first_line}": {str(e)}')
if task := get_task(task_key, pr):
tasks.add(task)
- if AHA_ADDITIONAL_RELEASES_TAG:
- task.set_releases_tags(list(set(task.releases_tags + [AHA_ADDITIONAL_RELEASES_TAG])))
return list(tasks)
-def get_feature_flags() -> list[str]:
- result = []
+def get_feature_flags() -> dict:
if LAUNCHDARKLY_SDK_KEY:
response = requests.get(
url="https://sdk.launchdarkly.com/sdk/latest-all",
@@ -286,35 +200,42 @@ def get_feature_flags() -> list[str]:
},
timeout=30,
)
- for key, flag in response.json().get('flags', {}).items():
- if not flag.get('on'):
- result.append(
- f'- [{key}]'
- f'(https://app.launchdarkly.com/default/{LAUNCHDARKLY_ENVIRONMENT}/features/{key}/targeting)')
- return result
+ return response.json().get('flags', {})
+ return {}
-def missing_tasks(left: list[AhaFeature], right: list[AhaFeature]) -> list[AhaFeature]:
+def missing_tasks(left: list[JiraIssue], right: list[JiraIssue]) -> list[JiraIssue]:
r_keys = [x.key for x in right]
missing = [task for task in left if task.key not in r_keys]
missing_sorted = sorted(missing, key=lambda x: int(x.key.split('-')[-1]))
return missing_sorted
-def sort_task_by_label(tasks: list[AhaFeature]) -> dict[str, list[AhaFeature]]:
+def sort_task_by_label(tasks: list[JiraIssue]) -> dict[str, list[JiraIssue]]:
result = {}
for task in tasks:
result[task.label] = result.get(task.label, []) + [task]
return result
-def render_tasks_md(tasks: list[AhaFeature]) -> list[str]:
+def render_link_md(text: str, link: str) -> str:
+ return f"[{text}]({link})"
+
+
+def render_tasks_md(tasks: list[JiraIssue]) -> list[str]:
result = []
for task in tasks:
- line = f'- {task.desc} [{task.key}]({task.link})'
- if task.pr:
- line += f' (#{task.pr})'
- result.append(line)
+ summary = task.desc.replace('\n', ' ')
+ result.append(f'- {summary} {render_link_md(task.key, task.link)}')
+ return result
+
+
+def render_ffs_md(ffs: list) -> list[str]:
+ result = []
+ for ff in ffs:
+ key = ff.get('key')
+ link = ff_link(ff)
+ result.append(f'- {render_link_md(key, link)}')
return result
@@ -340,15 +261,18 @@ def render_add_header_md(title: str, lines: list[str]) -> list[str]:
def render_output_md(
gh_release,
jira_release,
- aha_release,
- sorted_release_tasks: dict[str, list[AhaFeature]],
- missing_in_gh: list[AhaFeature],
- missing_in_tracker: list[AhaFeature],
- missing_release_note_field: list[AhaFeature],
- turned_off_feature_flags: list[str],
+ sorted_release_tasks: dict[str, list[JiraIssue]],
+ missing_in_gh: list[JiraIssue],
+ missing_in_tracker: list[JiraIssue],
+ missing_release_note_field: list[JiraIssue],
+ turned_off_feature_flags: list,
+ helm_chart_version: str = None,
) -> str:
release_notes_lines = []
+ if helm_chart_version:
+ release_notes_lines.append(f'Helm Chart version: {helm_chart_version}')
+
for label, tasks in sorted(sorted_release_tasks.items(),
key=lambda x: LABEL_SORT.index(x[0]) if x[0] in LABEL_SORT else 100):
release_notes_lines.extend(
@@ -360,7 +284,7 @@ def render_output_md(
comment = []
- comment.append(f'Full Changelog: [{PREVIOUS_REF}...{RELEASE_VERSION}]({gh_release.diff_url})')
+ comment.append(f'Full Changelog: [{PREVIOUS_REF}...{RELEASE_VERSION}]({gh_release.html_url})')
comment.append(
f'This changelog was updated in response to a push of {CURRENT_REF} [Workflow run]({WORKFLOW_RUN_LINK})')
comment.append('')
@@ -370,10 +294,6 @@ def render_output_md(
f'{jira_release.id}/tab/release-report-all-issues)')
else:
comment.append('Jira Release not found')
- if aha_release:
- comment.append(f'[Aha! Release {RELEASE_VERSION}]({aha_release.get("url", "")})')
- else:
- comment.append('Aha! Release not found')
if len(missing_in_tracker) == 0:
comment.append('Release Notes are generated based on git log: No tasks found in Task Tracker.')
@@ -405,7 +325,7 @@ def render_output_md(
comment.extend(
render_add_spoiler_md(
f'Turned off Feature Flags ({len(turned_off_feature_flags)})',
- turned_off_feature_flags
+ render_ffs_md(turned_off_feature_flags)
)
)
@@ -420,7 +340,7 @@ def render_output_md(
def render_output_json(
- sorted_release_tasks: dict[str, list[AhaFeature]],
+ sorted_release_tasks: dict[str, list[JiraIssue]],
) -> dict:
sorted_release_tasks_json = {}
for label, tasks in sorted_release_tasks.items():
@@ -431,30 +351,29 @@ def render_output_json(
return result
+def get_helm_chart_version(repo: str, path: str) -> str or None:
+ chart_repo = github_client.get_repo(repo)
+ content = chart_repo.get_contents(path)
+ version_regexp = re.compile(r'version:\s*(.*)')
+ match = re.search(version_regexp, content.decoded_content.decode('utf-8'))
+ return match.group(1)
+
+
def main():
gh_release = get_github_release(PREVIOUS_REF, CURRENT_REF)
- print(f"{gh_release.html_url}")
+ print(f"Compare url: {gh_release.html_url}")
print(f"Ahead by {gh_release.ahead_by}")
print(f"Behind by {gh_release.behind_by}")
print(f"Merge base commit: {gh_release.merge_base_commit}")
print(f"Commits: {gh_release.commits}")
- gh_release_tasks = get_github_release_tasks(gh_release.commits)
- aha_release = None
- aha_release_features = []
- if AHA_FETCH_STRATEGY == 'PARKING_LOT':
- aha_release = get_aha_release(AHA_PRODUCT, RELEASE_VERSION)
- if aha_release:
- aha_release_features = get_aha_release_features(aha_release.get("reference_num", None))
- print(f"Aha! Release {aha_release.get('url', '')}")
- else:
- print("Aha! Release not found")
- else:
- if AHA_TAG:
- aha_release = {'url': f'{AHA_SERVER}/api/v1/features?tag={AHA_TAG.replace(" ", "%20")}'}
- aha_release_features = get_aha_release_features_by_tag(AHA_TAG)
- else:
- print("AHA TAG is not specified")
+ global FEATURE_FLAGS
+ try:
+ FEATURE_FLAGS = get_feature_flags()
+ except Exception as e:
+ print(f'Failed to fetch Feature Flags: {e}')
+
+ gh_release_tasks = get_github_release_tasks(gh_release.commits)
jira_release = get_jira_release(JIRA_PROJECT, RELEASE_VERSION)
jira_release_issues = []
@@ -466,7 +385,7 @@ def main():
else:
print("Jira Release not found")
- tracker_release_tasks = jira_release_issues + aha_release_features
+ tracker_release_tasks = jira_release_issues
if tracker_release_tasks:
print(f"{len(tracker_release_tasks)} tasks found in Task Tracker")
@@ -482,21 +401,25 @@ def main():
missing_in_gh = []
missing_in_tracker = []
missing_release_note_field = [x for x in tracker_release_tasks if not x.release_note]
- turned_off_feature_flags = []
- try:
- turned_off_feature_flags = get_feature_flags()
- except Exception as e:
- print(f'Failed to fetch Feature Flags: {e}')
+
+ turned_off_feature_flags = [ff for name, ff in FEATURE_FLAGS.items() if not ff_is_on(ff)]
+
+ helm_chart_version = None
+ if HELM_CHART_REPO and HELM_CHART_PATH:
+ try:
+ helm_chart_version = get_helm_chart_version(HELM_CHART_REPO, HELM_CHART_PATH)
+ except Exception as e:
+ print(f'Failed to fetch Helm Chart Version: {e}')
output_md = render_output_md(
gh_release,
jira_release,
- aha_release,
sorted_release_tasks,
missing_in_gh,
missing_in_tracker,
missing_release_note_field,
turned_off_feature_flags,
+ helm_chart_version=helm_chart_version,
)
if OUTPUT_FILE_MD:
with open(OUTPUT_FILE_MD, 'w') as f:
diff --git a/.github/workflows/cicd_pipeline.yml b/.github/workflows/cicd_pipeline.yml
index 90e79eea5ab..40b2fca3f30 100644
--- a/.github/workflows/cicd_pipeline.yml
+++ b/.github/workflows/cicd_pipeline.yml
@@ -335,13 +335,6 @@ jobs:
CURRENT_REF: "${{ github.event.after }}"
GH_REPO: "${{ github.repository }}"
GH_TOKEN: "${{ secrets.GIT_PAT }}"
- AHA_SERVER: "https://labelstudio.aha.io"
- AHA_TOKEN: "${{ secrets.AHA_TOKEN }}"
- AHA_PRODUCT: "LSDV"
- AHA_RN_FIELD: "release_notes"
- AHA_FETCH_STRATEGY: "TAG"
- AHA_TAG: "LS ${{ steps.create-draft-release.outputs.tag_name }}"
- AHA_ADDITIONAL_RELEASES_TAG: "oss"
JIRA_SERVER: "${{ vars.JIRA_SERVER }}"
JIRA_USERNAME: "${{ secrets.JIRA_USERNAME }}"
JIRA_TOKEN: "${{ secrets.JIRA_TOKEN }}"
diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml
deleted file mode 100644
index 31cd8b65620..00000000000
--- a/.github/workflows/pr-labeler.yml
+++ /dev/null
@@ -1,111 +0,0 @@
-name: "PR labeler"
-
-on:
- pull_request_target:
- types:
- - opened
- - edited
- - reopened
- - synchronize
- - ready_for_review
- branches:
- - master
- - develop
- - 'release/**'
- - 'lse-release/**'
- - 'ls-release/**'
-
-env:
- ACTIONS_STEP_DEBUG: '${{ secrets.ACTIONS_STEP_DEBUG }}'
-
-jobs:
- autolabel:
- name: "PR label validator"
- runs-on: ubuntu-latest
- permissions:
- pull-requests: write
- steps:
-
- - uses: hmarr/debug-action@v3.0.0
-
- - name: "Validate PR's title"
- uses: thehanimo/pr-title-checker@v1.4.2
- with:
- GITHUB_TOKEN: ${{ github.token }}
- pass_on_octokit_error: false
- configuration_path: ".github/pr-title-checker-config.json"
-
- - name: "Set PR's label based on title"
- uses: release-drafter/release-drafter@v6.0.0
- with:
- disable-releaser: true
- config-name: autolabeler.yml
- env:
- GITHUB_TOKEN: ${{ github.token }}
-
- release_field:
- name: "Set Aha! Releases field"
- runs-on: ubuntu-latest
- steps:
- - uses: hmarr/debug-action@v3.0.0
-
- - name: Set field
- shell: bash
- env:
- COMMIT_MESSAGE: ${{ github.event.pull_request.title }}
- AHA_SERVER: "https://labelstudio.aha.io"
- AHA_TOKEN: "${{ secrets.AHA_TOKEN }}"
- AHA_RELEASES_FIELD: releases
- TAGS: '["oss"]'
- run: |
- set -euo pipefail ${ACTIONS_STEP_DEBUG:+-x}
-
- regex="([a-zA-Z]+):[[:space:]]*([a-zA-Z0-9-]+)?:"
- if [[ $COMMIT_MESSAGE =~ $regex ]]; then
- export TICKET="${BASH_REMATCH[2]}"
- if curl -fsSL \
- --request GET \
- --url "${AHA_SERVER}/api/v1/features/${TICKET}" \
- --header "Authorization: Bearer ${AHA_TOKEN}" \
- --header 'Accept: application/json'; then
- cur_tags=$(curl -sSL \
- --request GET \
- --url "${AHA_SERVER}/api/v1/features/${TICKET}" \
- --header "Authorization: Bearer ${AHA_TOKEN}" \
- --header "Content-Type: application/json" \
- --header 'Accept: application/json' |
- jq -ecr '[ .feature.custom_fields[] | select(.key=="releases") | .value ]')
- echo "Current tags: ${cur_tags}"
- new_tags=$(jq -ecr ". += ${TAGS} | flatten | unique" <<< $cur_tags)
- echo "New tags: ${new_tags}"
- curl -sSL \
- --request PUT \
- --url "${AHA_SERVER}/api/v1/features/${TICKET}" \
- --data "{\"feature\":{\"custom_fields\":{\"releases\":${new_tags}}}}" \
- --header "Authorization: Bearer ${AHA_TOKEN}" \
- --header "Content-Type: application/json" \
- --header 'Accept: application/json'
- elif curl -fsSL \
- --request GET \
- --url "${AHA_SERVER}/api/v1/requirements/${TICKET}" \
- --header "Authorization: Bearer ${AHA_TOKEN}" \
- --header 'Accept: application/json'; then
- cur_tags=$(curl -sSL \
- --request GET \
- --url "${AHA_SERVER}/api/v1/requirements/${TICKET}" \
- --header "Authorization: Bearer ${AHA_TOKEN}" \
- --header "Content-Type: application/json" \
- --header 'Accept: application/json' |
- jq -ecr '[ .requirement.custom_fields[] | select(.key=="releases") | .value ]')
- echo "Current tags: ${cur_tags}"
- new_tags=$(jq -ecr ". += ${TAGS} | flatten | unique" <<< $cur_tags)
- echo "New tags: ${new_tags}"
- curl -sSL \
- --request PUT \
- --url "${AHA_SERVER}/api/v1/requirements/${TICKET}" \
- --data "{\"requirement\":{\"custom_fields\":{\"releases\":${new_tags}}}}" \
- --header "Authorization: Bearer ${AHA_TOKEN}" \
- --header "Content-Type: application/json" \
- --header 'Accept: application/json'
- fi
- fi
diff --git a/.github/workflows/validator-pull-request-labeler.yml b/.github/workflows/validator-pull-request-labeler.yml
new file mode 100644
index 00000000000..dfdb774ac8d
--- /dev/null
+++ b/.github/workflows/validator-pull-request-labeler.yml
@@ -0,0 +1,44 @@
+name: "Check"
+
+on:
+ pull_request_target:
+ types:
+ - opened
+ - edited
+ - reopened
+ - synchronize
+ - ready_for_review
+ branches:
+ - master
+ - develop
+ - 'release/**'
+ - 'lse-release/**'
+ - 'ls-release/**'
+
+env:
+ ACTIONS_STEP_DEBUG: '${{ secrets.ACTIONS_STEP_DEBUG }}'
+
+jobs:
+ autolabel:
+ name: "PR label validator"
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+
+ - uses: hmarr/debug-action@v3.0.0
+
+ - name: "Validate PR's title"
+ uses: thehanimo/pr-title-checker@v1.4.2
+ with:
+ GITHUB_TOKEN: ${{ github.token }}
+ pass_on_octokit_error: false
+ configuration_path: ".github/pr-title-checker-config.json"
+
+ - name: "Set PR's label based on title"
+ uses: release-drafter/release-drafter@v6.0.0
+ with:
+ disable-releaser: true
+ config-name: autolabeler.yml
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
diff --git a/label_studio/feature_flags.json b/label_studio/feature_flags.json
index 81f00dbac65..66122b6dbbc 100644
--- a/label_studio/feature_flags.json
+++ b/label_studio/feature_flags.json
@@ -138,7 +138,7 @@
},
"feat_front_dev_4008_quick_task_open_short": {
"key": "feat_front_dev_4008_quick_task_open_short",
- "on": false,
+ "on": true,
"prerequisites": [],
"targets": [],
"contextTargets": [],
@@ -160,7 +160,7 @@
"trackEvents": false,
"trackEventsFallthrough": false,
"debugEventsUntilDate": null,
- "version": 2,
+ "version": 3,
"deleted": false
},
"feat_front_lsdv_4583_multi_image_segmentation_short": {
@@ -595,34 +595,6 @@
"version": 2,
"deleted": false
},
- "ff_click_new_project_action_short": {
- "key": "ff_click_new_project_action_short",
- "on": true,
- "prerequisites": [],
- "targets": [],
- "contextTargets": [],
- "rules": [],
- "fallthrough": {
- "variation": 0
- },
- "offVariation": 0,
- "variations": [
- "Control",
- "Treatment 1",
- "Treatment 2"
- ],
- "clientSideAvailability": {
- "usingMobileKey": false,
- "usingEnvironmentId": false
- },
- "clientSide": false,
- "salt": "32c224eac7a14fe5a8ee046dd0a00619",
- "trackEvents": false,
- "trackEventsFallthrough": false,
- "debugEventsUntilDate": null,
- "version": 3,
- "deleted": false
- },
"ff_dev_2007_dev_2008_dynamic_tag_children_250322_short": {
"key": "ff_dev_2007_dev_2008_dynamic_tag_children_250322_short",
"on": true,
@@ -1752,7 +1724,7 @@
"contextTargets": [],
"rules": [],
"fallthrough": {
- "variation": 1
+ "variation": 0
},
"offVariation": 1,
"variations": [
@@ -1768,7 +1740,7 @@
"trackEvents": false,
"trackEventsFallthrough": false,
"debugEventsUntilDate": null,
- "version": 7,
+ "version": 8,
"deleted": false
},
"fflag_feat_all_lsdv_e_295_project_level_roles_via_saml_scim_ldap_short": {
diff --git a/label_studio/ml/api.py b/label_studio/ml/api.py
index a04f5746a22..f82f78e0edd 100644
--- a/label_studio/ml/api.py
+++ b/label_studio/ml/api.py
@@ -88,6 +88,15 @@ def perform_create(self, serializer):
ml_backend = serializer.save()
ml_backend.update_state()
+ project = ml_backend.project
+
+ # In case we are adding the model, let's set it as the default
+ # to obtain predictions. This approach is consistent with uploading
+ # offline predictions, which would be set automatically.
+ if project.show_collab_predictions and not project.model_version:
+ project.model_version = ml_backend.title
+ project.save(update_fields=['model_version'])
+
@method_decorator(
name='patch',
diff --git a/label_studio/projects/serializers.py b/label_studio/projects/serializers.py
index 3d59467c6ee..ca382b07848 100644
--- a/label_studio/projects/serializers.py
+++ b/label_studio/projects/serializers.py
@@ -94,6 +94,7 @@ def to_internal_value(self, data):
# FIXME: remake this logic with start_training_on_annotation_update
initial_data = data
data = super().to_internal_value(data)
+
if 'start_training_on_annotation_update' in initial_data:
data['min_annotations_to_start_training'] = int(initial_data['start_training_on_annotation_update'])
@@ -186,7 +187,7 @@ def validate_model_version(self, value):
return value
def update(self, instance, validated_data):
- if not validated_data.get('show_collab_predictions'):
+ if validated_data.get('show_collab_predictions') is False:
instance.model_version = ''
return super().update(instance, validated_data)
diff --git a/label_studio/tests/ml/test_api.py b/label_studio/tests/ml/test_api.py
index eece364782a..bb4c1ce28a6 100644
--- a/label_studio/tests/ml/test_api.py
+++ b/label_studio/tests/ml/test_api.py
@@ -6,6 +6,10 @@
from label_studio.tests.utils import make_project, register_ml_backend_mock
+ORIG_MODEL_NAME = 'basic_ml_backend'
+PROJECT_CONFIG = """