Skip to content

ci: PLT-608: Deploy long version #14044

ci: PLT-608: Deploy long version

ci: PLT-608: Deploy long version #14044

Workflow file for this run

name: "CI/CD Pipeline"
on:
push:
branches:
- develop
- 'ls-release/**'
pull_request:
types:
- opened
- synchronize
- reopened
- ready_for_review
branches:
- develop
- 'ls-release/**'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.pull_request.head.ref || github.ref }}
cancel-in-progress: true
env:
RELEASE_BRANCH_PREFIX: "ls-release/"
jobs:
changed_files:
name: "Changed files"
runs-on: ubuntu-latest
outputs:
src: ${{ steps.changes.outputs.src }}
frontend: ${{ steps.changes.outputs.frontend }}
docker: ${{ steps.changes.outputs.docker }}
commit-message: ${{ steps.commit-details.outputs.commit-message }}
timeout-minutes: 25
steps:
- uses: hmarr/[email protected]
- name: Checkout
if: github.event_name == 'push'
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
src:
- 'label_studio/!(frontend)/**'
- 'poetry.lock'
- 'pyproject.toml'
- 'scripts/*.py'
- '.github/workflows/bandit.yml'
- '.github/workflows/tests.yml'
- '.github/workflows/test_conda.yml'
- '.github/workflows/test_migrations.yml'
frontend:
- 'web/**'
- '.github/workflows/frontend-build.yml'
- '.github/workflows/tests-yarn-unit.yml'
- '.github/workflows/tests-yarn-integration.yml'
- '.github/workflows/tests-yarn-e2e.yml'
docker:
- 'label_studio/**'
- 'web/**'
- 'deploy/**'
- 'Dockerfile**'
- 'poetry.lock'
- 'pyproject.toml'
- '.github/workflows/cicd_pipeline.yml'
- '.github/workflows/docker-build.yml'
- uses: actions/github-script@v7
id: commit-details
with:
script: |
const { repo, owner } = context.repo;
const { data: commit } = await github.rest.repos.getCommit({
owner,
repo,
ref: '${{ github.event.pull_request.head.sha || github.event.after }}'
});
console.log(`Last commit message is "${commit.commit.message}"`)
core.setOutput("commit-message", commit.commit.message);
gitleaks:
name: "Linter"
if: github.event_name == 'pull_request'
uses: ./.github/workflows/gitleaks.yml
with:
head_sha: ${{ github.sha }}
base_sha: ${{ github.event.pull_request.base.sha || github.event.before }}
bandit:
name: "Linter"
needs:
- changed_files
if: needs.changed_files.outputs.src == 'true'
uses: ./.github/workflows/bandit.yml
with:
head_sha: ${{ github.event.pull_request.head.sha || github.event.after }}
ruff:
name: "Linter"
needs:
- changed_files
if: needs.changed_files.outputs.src == 'true'
uses: ./.github/workflows/ruff.yml
with:
head_sha: ${{ github.event.pull_request.head.sha || github.event.after }}
blue:
name: "Linter"
needs:
- changed_files
if: needs.changed_files.outputs.src == 'true'
uses: ./.github/workflows/blue.yml
with:
head_sha: ${{ github.event.pull_request.head.sha || github.event.after }}
biome:
name: "Linter"
needs:
- changed_files
if: needs.changed_files.outputs.frontend == 'true'
uses: ./.github/workflows/biome.yml
with:
head_sha: ${{ github.event.pull_request.head.sha || github.event.after }}
stylelint:
name: "Linter"
needs:
- changed_files
if: needs.changed_files.outputs.frontend == 'true'
uses: ./.github/workflows/stylelint.yml
with:
head_sha: ${{ github.event.pull_request.head.sha || github.event.after }}
build-frontend-docs:
name: "Build"
needs:
- changed_files
if: |
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.fork == false &&
needs.changed_files.outputs.frontend == 'true' &&
!startsWith(needs.changed_files.outputs.commit-message, 'ci: Build frontend')
permissions:
contents: write
uses: ./.github/workflows/frontend-build.yml
with:
ref: ${{ github.event.pull_request.head.ref || github.ref }}
build_static: false
generate_version_files: false
generate_doc_tags_files: true
secrets: inherit
build-frontend-static:
name: "Build"
needs:
- changed_files
if: |
github.event_name == 'push' &&
needs.changed_files.outputs.frontend == 'true' &&
!startsWith(needs.changed_files.outputs.commit-message, 'ci: Build frontend')
permissions:
contents: write
uses: ./.github/workflows/frontend-build.yml
with:
ref: ${{ github.event.pull_request.head.ref || github.ref }}
build_static: true
generate_version_files: true
generate_doc_tags_files: true
secrets: inherit
build-docker:
name: "Build"
needs:
- changed_files
if: |
github.event_name == 'push' &&
( ( github.ref_name == 'develop' && needs.changed_files.outputs.docker == 'true' )
|| startsWith(github.ref_name, 'ls-release/') )
permissions:
contents: read
checks: write
uses: ./.github/workflows/docker-build.yml
with:
sha: ${{ github.event.pull_request.head.sha || github.event.after }}
branch_name: ${{ github.event.pull_request.head.ref || github.ref_name }}
secrets: inherit
deploy:
name: "Deploy"
if: startsWith(github.ref_name, 'ls-release/') || github.ref_name == 'develop'
runs-on: ubuntu-latest
needs:
- build-docker
steps:
- uses: actions/github-script@v7
env:
DOCKER_IMAGE_VERSION: ${{ needs.build-docker.outputs.build_version }}
RELEASE_NAME: ${{ startsWith(github.ref_name, 'ls-release/') && 'ls-release' || 'ls-develop' }}
with:
github-token: ${{ secrets.GIT_PAT }}
script: |
const docker_image_version = process.env.DOCKER_IMAGE_VERSION;
const release_name = process.env.RELEASE_NAME;
github.rest.actions.createWorkflowDispatch({
owner: "HumanSignal",
repo: "label-studio-enterprise",
workflow_id: "argocd-deploy.yml",
ref: "develop",
inputs: {
docker_image_version: docker_image_version,
release_name: release_name,
template_name: "lso",
}
});
pytest:
name: "Tests"
needs:
- changed_files
if: needs.changed_files.outputs.src == 'true'
uses: ./.github/workflows/tests.yml
with:
head_sha: ${{ github.event.pull_request.head.sha || github.event.after }}
secrets: inherit
migrations:
name: "Tests"
needs:
- changed_files
if: needs.changed_files.outputs.src == 'true'
uses: ./.github/workflows/test_migrations.yml
with:
head_sha: ${{ github.event.pull_request.head.sha || github.event.after }}
secrets: inherit
conda-test:
name: "Tests"
needs:
- changed_files
if: needs.changed_files.outputs.src == 'true'
uses: ./.github/workflows/test_conda.yml
with:
head_sha: ${{ github.event.pull_request.head.sha || github.event.after }}
secrets: inherit
draft-release:
name: "Draft Release"
if: |
github.event_name == 'push' &&
startsWith(github.ref_name, 'ls-release/')
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
id: ${{ steps.update-draft-release.outputs.id }}
rc-version: ${{ steps.create-draft-release.outputs.rc-version }}
steps:
- uses: hmarr/[email protected]
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GIT_PAT }}
ref: ${{ github.sha }}
fetch-depth: 0
- name: Checkout Actions Hub
uses: actions/checkout@v4
with:
token: ${{ secrets.GIT_PAT }}
repository: HumanSignal/actions-hub
path: ./.github/actions-hub
- name: Create release draft
uses: actions/github-script@v7
id: create-draft-release
env:
TARGET_COMMITISH: "${{ github.ref_name }}"
RELEASE_BRANCH_PREFIX: "${{ env.RELEASE_BRANCH_PREFIX }}"
DEFAULT_BRANCH: "${{ github.event.repository.default_branch }}"
with:
script: |
const { repo, owner } = context.repo;
const target_commitish = process.env.TARGET_COMMITISH;
const default_branch = process.env.DEFAULT_BRANCH;
let version = target_commitish.replace(process.env.RELEASE_BRANCH_PREFIX, '')
const regexp = '^[v]?([0-9]+)\.([0-9]+)\.([0-9]+)(\.post([0-9]+))?$';
const {data: compare} = await github.rest.repos.compareCommits({
owner,
repo,
base: default_branch,
head: target_commitish,
});
const rc_version = `${version}rc${ compare.ahead_by }`
console.log(`rc-version: ${rc_version}`)
core.setOutput("rc-version", rc_version);
function compareVersions(a, b) {
if (a[1] === b[1])
if (a[2] === b[2])
if (a[3] === b[3])
return (+a[5] || -1) - (+b[5] || -1)
else
return +a[3] - b[3]
else
return +a[2] - b[2]
else
return +a[1] - b[1]
}
const versionMatch = version.match(regexp)
if (!versionMatch) {
core.setFailed(`Version "${version}" from branch "${target_commitish}" does not match the regexp ${regexp}`)
process.exit()
}
const tags = await github.paginate(
github.rest.repos.listTags,
{
owner,
repo,
per_page: 100
},
(response) => response.data
);
console.log(`Tags:`)
console.log(tags.map(e => e.name))
const matchedTags = tags.filter(e => e.name.indexOf(version) !== -1)
console.log(`Tags for ${version}:`)
console.log(matchedTags.map(e => e.name))
if (matchedTags.length !== 0) {
let newHotfixNumber = 0
for (let matchedTag of matchedTags) {
const matchVersion = matchedTag.name.match('^[v]?([0-9]+)\.([0-9]+)\.([0-9]+)(.post([0-9]+))?$')
if (matchVersion && matchVersion[5]) {
const hotfixNumber = parseInt(matchVersion[5])
if (newHotfixNumber <= hotfixNumber) {
newHotfixNumber = hotfixNumber + 1
}
}
}
version = `${version}.post${newHotfixNumber}`
}
console.log(`New version: ${version}`)
const rawTags = tags.map(e => e.name)
const tagsWithNew = [...rawTags, version]
const sortedTags = tagsWithNew
.filter(e => e.match(regexp))
.map((e => e.match(regexp)))
.sort(compareVersions)
.reverse()
.map(e => e[0])
const previousTag = sortedTags[sortedTags.indexOf(version)+1]
console.log(`Previous version: ${previousTag}`)
console.log('Find or Create a Draft release')
const releases = await github.paginate(
github.rest.repos.listReleases,
{
owner,
repo,
per_page: 100
},
(response) => response.data
);
let release = releases.find(e => target_commitish.endsWith(e.target_commitish) && e.draft)
if (release) {
console.log(`Draft release already exist ${release.html_url}`)
} else {
console.log(`Draft release is not found creating a new one`)
const {data: newDraftRelease} = await github.rest.repos.createRelease({
owner,
repo,
draft: true,
prerelease: false,
name: version,
tag_name: version,
target_commitish: target_commitish,
});
console.log(`Draft release is created ${newDraftRelease.html_url}`)
release = newDraftRelease;
core.setOutput("created", true);
}
core.setOutput("id", release.id);
core.setOutput("tag_name", release.tag_name);
- name: Get previous GitHub ref
id: previous-tag
env:
RELEASE_BRANCH: "${{ github.ref_name }}"
run: |
set -eux
version="${RELEASE_BRANCH/#ls-release\//}"
previous_tag=$(git tag --sort=-committerdate | head -n1)
echo "previous_tag_name=tags/${previous_tag}" >> $GITHUB_OUTPUT
- name: Set Jira fix version
uses: ./.github/actions-hub/actions/jira-set-fix-version
continue-on-error: true
with:
github_token: "${{ secrets.GIT_PAT }}"
github_repository: "${{ github.repository }}"
github_previous_ref: "${{ steps.previous-tag.outputs.previous_tag_name }}"
github_current_ref: "${{ github.event.after }}"
jira_server: "${{ vars.JIRA_SERVER }}"
jira_username: "${{ secrets.JIRA_USERNAME }}"
jira_token: "${{ secrets.JIRA_TOKEN }}"
jira_fix_version: "LS OpenSource/${{ steps.create-draft-release.outputs.tag_name }}"
- name: Generate release changelog
id: changelog_md
uses: ./.github/actions-hub/actions/github-generate-changelog
with:
release_version: "${{ steps.create-draft-release.outputs.tag_name }}"
previous_ref: "${{ steps.previous-tag.outputs.previous_tag_name }}"
current_ref: "${{ github.event.after }}"
github_token: "${{ secrets.GIT_PAT }}"
jira_server: "${{ vars.JIRA_SERVER }}"
jira_username: "${{ secrets.JIRA_USERNAME }}"
jira_token: "${{ secrets.JIRA_TOKEN }}"
jira_release_prefix: "LS OpenSource"
launchdarkly_sdk_key: "${{ secrets.LAUNCHDARKLY_COMMUNITY_SDK_KEY }}"
launchdarkly_environment: "community"
helm_chart_repo: "HumanSignal/charts"
helm_chart_path: "heartex/label-studio/Chart.yaml"
- name: Update Draft Release
uses: actions/github-script@v7
id: update-draft-release
with:
github-token: ${{ secrets.GIT_PAT }}
script: |
const { repo, owner } = context.repo;
const { data: release } = await github.rest.repos.updateRelease({
owner,
repo,
release_id: '${{ steps.create-draft-release.outputs.id }}',
draft: true,
prerelease: false,
name: '${{ steps.create-draft-release.outputs.tag_name }}',
tag_name: '${{ steps.create-draft-release.outputs.tag_name }}',
target_commitish: '${{ github.ref_name }}',
body: atob(`${{ steps.changelog_md.outputs.changelog_msg_b64 }}`)
});
console.log(`Draft release is updated: ${release.html_url}`)
core.setOutput("id", release.id);
core.setOutput("tag_name", release.tag_name);
core.setOutput("html_url", release.html_url);
build-pypi:
name: "Build"
needs:
- draft-release
if: |
github.event_name == 'push' &&
startsWith(github.ref_name, 'ls-release/')
permissions:
contents: write
uses: ./.github/workflows/build_pypi.yml
with:
version: ${{ needs.draft-release.outputs.rc-version }}
ref: ${{ github.ref_name }}
release-id: ${{ needs.draft-release.outputs.id }}
secrets: inherit
tests-yarn-unit:
name: "Tests"
needs:
- changed_files
if: needs.changed_files.outputs.frontend == 'true'
uses: ./.github/workflows/tests-yarn-unit.yml
with:
head_sha: ${{ github.event.pull_request.head.sha || github.event.after }}
secrets: inherit
tests-yarn-integration:
name: "Tests"
needs:
- changed_files
if: needs.changed_files.outputs.frontend == 'true'
uses: ./.github/workflows/tests-yarn-integration.yml
with:
head_sha: ${{ github.event.pull_request.head.sha || github.event.after }}
secrets: inherit
tests-yarn-e2e:
name: "Tests"
needs:
- changed_files
if: needs.changed_files.outputs.frontend == 'true'
uses: ./.github/workflows/tests-yarn-e2e.yml
with:
head_sha: ${{ github.event.pull_request.head.sha || github.event.after }}
secrets: inherit
check_gate:
name: "Ready to merge"
if: always()
needs:
- gitleaks
- bandit
- ruff
- blue
- biome
- stylelint
- pytest
- migrations
- build-docker
- tests-yarn-unit
- tests-yarn-integration
- tests-yarn-e2e
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
allowed-skips: gitleaks, bandit, ruff, blue, biome, stylelint, pytest, migrations, conda-test, build-docker, tests-yarn-unit, tests-yarn-integration, tests-yarn-e2e
jobs: ${{ toJSON(needs) }}
dependabot-auto-merge:
name: "Auto Merge dependabot PR"
if: |
always() &&
needs.check_gate.result == 'success' &&
github.event_name == 'pull_request' &&
github.event.pull_request.user.login == 'dependabot[bot]' &&
( startsWith(github.head_ref, 'dependabot/npm_and_yarn/') || startsWith(github.head_ref, 'dependabot/pip/') )
runs-on: ubuntu-latest
needs:
- check_gate
steps:
- name: Enable auto-merge for Dependabot PRs
run: gh pr merge --admin --squash "${PR_URL}"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.GIT_PAT }}