Skip to content
Closed
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
86 changes: 86 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: CI
run-name: CI · ${{ github.event.pull_request.title || github.ref_name }}

on:
pull_request:
Expand All @@ -14,7 +15,62 @@ concurrency:
cancel-in-progress: true

jobs:
changes:
name: Change scope
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
android: ${{ steps.scope.outputs.android }}
native: ${{ steps.scope.outputs.native }}
python: ${{ steps.scope.outputs.python }}
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
persist-credentials: false
- name: Classify the complete change set
id: scope
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
EVENT_NAME: ${{ github.event_name }}
run: |
args=(--base "$BASE_SHA" --head "$HEAD_SHA" --github-output "$GITHUB_OUTPUT" --summary "$GITHUB_STEP_SUMMARY")
if [[ "$EVENT_NAME" == push ]]; then args+=(--push); fi
python3 scripts/ci_changes.py "${args[@]}"

ui-status:
name: Prepare manual UI status
needs: changes
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
statuses: write
steps:
- uses: actions/github-script@v8
env:
ANDROID_CHANGED: ${{ needs.changes.outputs.android }}
with:
script: |
const sha = context.payload.pull_request.head.sha;
const name = 'Selectel UI required';
const needed = process.env.ANDROID_CHANGED === 'true';
const statuses = await github.paginate(github.rest.repos.listCommitStatusesForRef, {
...context.repo, ref: sha, per_page: 100
});
// A CI rerun must preserve an existing UI result or active manual run.
if (!statuses.some(status => status.context === name)) {
await github.rest.repos.createCommitStatus({
...context.repo, sha, context: name, state: needed ? 'pending' : 'success',
description: needed ? 'Awaiting manual required UI run for this commit' : 'UI not required: no Android-impacting changes',
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/workflows/selectel-ui.yml`
});
}

native-tests:
needs: changes
if: needs.changes.outputs.native == 'true'
name: Native Go tests
runs-on: ubuntu-latest
timeout-minutes: 20
Expand All @@ -37,7 +93,37 @@ jobs:
- name: Run native tests with the race detector
run: bundle exec fastlane android native_tests

python-tests:
needs: changes
if: needs.changes.outputs.python == 'true'
name: Python tests and style
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@v6

- name: Set up Ruby and Fastlane
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.4.10"
bundler-cache: true

- uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
cache-dependency-path: requirements-dev.txt

- name: Install Python formatting tools
run: python -m pip install -r requirements-dev.txt

- name: Check Python formatting and runner contracts without a device
run: bundle exec fastlane android python_checks

android-tests:
needs: changes
if: needs.changes.outputs.android == 'true'
name: Android tests and checks
runs-on: ubuntu-latest
timeout-minutes: 45
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/pr-artifacts-comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ jobs:
return;
}

const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRun, {
...context.repo, run_id: run.id, filter: 'latest', per_page: 100
});
if (jobs.some(job => job.name === 'Android tests and checks' && job.conclusion === 'skipped')) {
core.notice('Android checks were skipped by the change filter; no APK artifacts are expected');
return;
}

const pullNumber = pullRequests[0].number;
const expectedArtifacts = {
debug: `megaproxy-pr-${pullNumber}-debug-apk`,
Expand Down
83 changes: 83 additions & 0 deletions .github/workflows/selectel-cleanup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Selectel lease recovery

on:
workflow_run:
workflows: [Selectel UI]
types: [completed]

permissions:
contents: read
actions: read
statuses: write

jobs:
release:
# Only our own branches can create leases. Do not run fork-provided code.
if: github.event.workflow_run.head_repository.full_name == github.repository
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out trusted default branch cleanup code
uses: actions/checkout@v6
with:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.4.10"
bundler-cache: true
- name: Find lease journal
id: journal
uses: actions/github-script@v8
with:
script: |
const run = context.payload.workflow_run;
const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, {
...context.repo, run_id: run.id
});
const expected = `selectel-lease-${run.id}-${run.run_attempt}`;
const journals = artifacts.filter(a => !a.expired && (a.name === expected || a.name.startsWith(expected + '-')));
core.setOutput('found', journals.length > 0);
core.setOutput('ids', journals.map(a => a.id).join(','));
- name: Download only the lease data
if: steps.journal.outputs.found == 'true'
uses: actions/download-artifact@v8
with:
artifact-ids: ${{ steps.journal.outputs.ids }}
path: .selectel
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ github.token }}
- name: Release any remaining lease
if: steps.journal.outputs.found == 'true'
env:
SELECTEL_USERNAME: ${{ secrets.SELECTEL_USERNAME }}
SELECTEL_PASSWORD: ${{ secrets.SELECTEL_PASSWORD }}
SELECTEL_ACCOUNT_ID: ${{ secrets.SELECTEL_ACCOUNT_ID }}
SELECTEL_PROJECT_ID: ${{ secrets.SELECTEL_PROJECT_ID }}
FASTLANE_SKIP_DOCS: "1"
run: bundle exec fastlane android selectel_release
- name: Close a failed or cancelled UI status
if: always() && github.event.workflow_run.conclusion != 'success'
uses: actions/github-script@v8
with:
script: |
const run = context.payload.workflow_run;
const match = /^Selectel UI · (required|additional) · /.exec(run.display_title || '');
if (!match) return;
const current = (await github.rest.actions.getWorkflowRun({
...context.repo, run_id: run.id
})).data;
if (current.run_attempt !== run.run_attempt) return;
const name = `Selectel UI ${match[1]}`;
const statuses = await github.paginate(github.rest.repos.listCommitStatusesForRef, {
...context.repo, ref: run.head_sha, per_page: 100
});
const latest = statuses.find(status => status.context === name);
const waitingUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/workflows/selectel-ui.yml`;
// Never overwrite a newer run's result for the same commit/profile.
if (latest && latest.target_url !== run.html_url && latest.target_url !== waitingUrl) return;
await github.rest.repos.createCommitStatus({
...context.repo, sha: run.head_sha, context: name, state: 'failure',
description: 'UI run failed or was cancelled; inspect lease recovery logs',
target_url: run.html_url
});
Loading
Loading