feat: BROS-573: Project soft deletion #22406
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "CI/CD Pipeline" | |
| on: | |
| push: | |
| branches: | |
| - '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@v5 | |
| 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/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@v8 | |
| 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 tag docs') | |
| permissions: | |
| contents: write | |
| uses: ./.github/workflows/create-tag-docs.yml | |
| with: | |
| ref: ${{ github.event.pull_request.head.ref || github.ref }} | |
| secrets: inherit | |
| build-docker: | |
| name: "Build" | |
| needs: | |
| - changed_files | |
| if: 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/') | |
| runs-on: ubuntu-latest | |
| needs: | |
| - build-docker | |
| steps: | |
| - uses: actions/github-script@v8 | |
| env: | |
| DOCKER_IMAGE_VERSION: ${{ needs.build-docker.outputs.build_version }} | |
| RELEASE_NAME: 'ls-release' | |
| 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@v5 | |
| with: | |
| token: ${{ secrets.GIT_PAT }} | |
| ref: ${{ github.sha }} | |
| fetch-depth: 0 | |
| - name: Checkout Actions Hub | |
| uses: actions/checkout@v5 | |
| with: | |
| token: ${{ secrets.GIT_PAT }} | |
| repository: HumanSignal/actions-hub | |
| path: ./.github/actions-hub | |
| - name: Create release draft | |
| uses: actions/github-script@v8 | |
| 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 | grep -v nightly | 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 | |
| uses: ./.github/actions-hub/actions/github-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_path: "label_studio/feature_flags.json" | |
| ignore_tags: "internal" | |
| helm_chart_repo: "HumanSignal/charts" | |
| helm_chart_path: "heartex/label-studio/Chart.yaml" | |
| - name: Update Draft Release | |
| uses: actions/github-script@v8 | |
| id: update-draft-release | |
| env: | |
| CHANGELOG: "${{ steps.changelog.outputs.changelog }}" | |
| with: | |
| github-token: ${{ secrets.GIT_PAT }} | |
| script: | | |
| const { repo, owner } = context.repo; | |
| const changelog = process.env.CHANGELOG; | |
| 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: changelog | |
| }); | |
| 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 | |
| allowed-failures: | | |
| [ | |
| "gitleaks" | |
| ] | |
| 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 }} |