2023-10-05 04:32:20 +00:00
# ## Description
#
# Uses [diffcalc-sheet-generator](https://github.com/smoogipoo/diffcalc-sheet-generator) to run two builds of osu and generate an SR/PP/Score comparison spreadsheet.
#
# ## Requirements
#
# Self-hosted runner with installed:
# - `docker >= 20.10.16`
# - `docker-compose >= 2.5.1`
# - `lbzip2`
# - `jq`
#
# ## Usage
#
# The workflow can be run in two ways:
# 1. Via workflow dispatch.
2023-10-17 11:15:00 +00:00
# 2. By an owner of the repository posting a pull request or issue comment containing `!diffcalc`.
# For pull requests, the workflow will assume the pull request as the target to compare against (i.e. the `OSU_B` variable).
2023-10-05 04:32:20 +00:00
# Any lines in the comment of the form `KEY=VALUE` are treated as variables for the generator.
#
# ## Google Service Account
#
# Spreadsheets are uploaded to a Google Service Account, and exposed with read-only permissions to the wider audience.
#
# 1. Create a project at https://console.cloud.google.com
# 2. Enable the `Google Sheets` and `Google Drive` APIs.
# 3. Create a Service Account
# 4. Generate a key in the JSON format.
# 5. Encode the key as base64 and store as an **actions secret** with name **`DIFFCALC_GOOGLE_CREDENTIALS`**
#
# ## Environment variables
#
# The default environment may be configured via **actions variables**.
#
# Refer to [the sample environment](https://github.com/smoogipoo/diffcalc-sheet-generator/blob/master/.env.sample), and prefix each variable with `DIFFCALC_` (e.g. `DIFFCALC_THREADS`, `DIFFCALC_INNODB_BUFFER_SIZE`, etc...).
name : Run difficulty calculation comparison
2023-10-05 04:20:07 +00:00
run-name : "${{ github.event_name == 'workflow_dispatch' && format('Manual run: {0}', inputs.osu-b) || 'Automatic comment trigger' }}"
2021-09-03 08:56:56 +00:00
on :
issue_comment :
2023-03-27 15:07:01 +00:00
types : [ created ]
2023-10-05 04:20:07 +00:00
workflow_dispatch :
inputs :
osu-b :
description : "The target build of ppy/osu"
type : string
required : true
ruleset :
description : "The ruleset to process"
type : choice
required : true
options :
- osu
- taiko
- catch
- mania
converts :
description : "Include converted beatmaps"
type : boolean
required : false
default : true
ranked-only :
description : "Only ranked beatmaps"
type : boolean
required : false
default : true
generators :
description : "Comma-separated list of generators (available: [sr, pp, score])"
type : string
required : false
default : 'pp,sr'
osu-a :
description : "The source build of ppy/osu"
type : string
required : false
default : 'latest'
difficulty-calculator-a :
description : "The source build of ppy/osu-difficulty-calculator"
type : string
required : false
default : 'latest'
difficulty-calculator-b :
description : "The target build of ppy/osu-difficulty-calculator"
type : string
required : false
default : 'latest'
score-processor-a :
description : "The source build of ppy/osu-queue-score-statistics"
type : string
required : false
default : 'latest'
score-processor-b :
description : "The target build of ppy/osu-queue-score-statistics"
type : string
required : false
default : 'latest'
permissions :
pull-requests : write
2021-09-03 08:56:56 +00:00
env :
2023-10-19 18:26:12 +00:00
EXECUTION_ID : execution-${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }}
2021-09-03 08:56:56 +00:00
jobs :
2023-10-17 11:15:00 +00:00
check-permissions :
name : Check permissions
2023-10-05 04:20:07 +00:00
runs-on : ubuntu-latest
2023-10-17 11:15:00 +00:00
if : ${{ github.event_name == 'workflow_dispatch' || contains(github.event.comment.body, '!diffcalc') }}
2023-10-05 04:20:07 +00:00
steps :
2023-10-17 11:15:00 +00:00
- name : Check permissions
2024-03-27 04:37:47 +00:00
run : |
2024-07-08 04:58:42 +00:00
ALLOWED_USERS=(smoogipoo peppy bdach frenzibyte)
2024-03-27 04:37:47 +00:00
for i in "${ALLOWED_USERS[@]}"; do
if [[ "${{ github.actor }}" == "$i" ]]; then
exit 0
fi
done
exit 1
2023-10-05 04:20:07 +00:00
create-comment :
name : Create PR comment
2023-10-17 11:15:00 +00:00
needs : check-permissions
2023-10-05 04:20:07 +00:00
runs-on : ubuntu-latest
2023-10-17 11:15:00 +00:00
if : ${{ github.event_name == 'issue_comment' && github.event.issue.pull_request }}
2023-10-05 04:20:07 +00:00
steps :
- name : Create comment
2024-03-28 15:25:03 +00:00
uses : thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0
2023-10-05 04:20:07 +00:00
with :
2023-10-19 18:26:12 +00:00
comment_tag : ${{ env.EXECUTION_ID }}
2023-10-05 04:20:07 +00:00
message : |
Difficulty calculation queued -- please wait! (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
*This comment will update on completion*
directory :
name : Prepare directory
2023-10-19 18:26:12 +00:00
needs : check-permissions
2021-09-20 17:27:21 +00:00
runs-on : self-hosted
outputs :
2023-10-05 04:20:07 +00:00
GENERATOR_DIR : ${{ steps.set-outputs.outputs.GENERATOR_DIR }}
GENERATOR_ENV : ${{ steps.set-outputs.outputs.GENERATOR_ENV }}
GOOGLE_CREDS_FILE : ${{ steps.set-outputs.outputs.GOOGLE_CREDS_FILE }}
2021-09-20 17:27:21 +00:00
steps :
2023-10-05 04:20:07 +00:00
- name : Checkout diffcalc-sheet-generator
2024-02-22 15:58:21 +00:00
uses : actions/checkout@v4
2023-10-05 04:20:07 +00:00
with :
2023-10-19 18:26:12 +00:00
path : ${{ env.EXECUTION_ID }}
2023-10-05 04:20:07 +00:00
repository : 'smoogipoo/diffcalc-sheet-generator'
- name : Set outputs
id : set-outputs
2021-09-20 17:27:21 +00:00
run : |
2023-10-19 18:26:12 +00:00
echo "GENERATOR_DIR=${{ github.workspace }}/${{ env.EXECUTION_ID }}" >> "${GITHUB_OUTPUT}"
echo "GENERATOR_ENV=${{ github.workspace }}/${{ env.EXECUTION_ID }}/.env" >> "${GITHUB_OUTPUT}"
echo "GOOGLE_CREDS_FILE=${{ github.workspace }}/${{ env.EXECUTION_ID }}/google-credentials.json" >> "${GITHUB_OUTPUT}"
2023-10-05 04:20:07 +00:00
environment :
name : Setup environment
needs : directory
runs-on : self-hosted
env :
VARS_JSON : ${{ toJSON(vars) }}
steps :
- name : Add base environment
run : |
# Required by diffcalc-sheet-generator
2023-10-20 02:24:16 +00:00
cp '${{ needs.directory.outputs.GENERATOR_DIR }}/.env.sample' "${{ needs.directory.outputs.GENERATOR_ENV }}"
2023-10-05 04:20:07 +00:00
# Add Google credentials
echo '${{ secrets.DIFFCALC_GOOGLE_CREDENTIALS }}' | base64 -d > "${{ needs.directory.outputs.GOOGLE_CREDS_FILE }}"
# Add repository variables
echo "${VARS_JSON}" | jq -c '. | to_entries | .[]' | while read -r line; do
opt=$(jq -r '.key' <<< ${line})
val=$(jq -r '.value' <<< ${line})
if [[ "${opt}" =~ ^DIFFCALC_ ]]; then
optNoPrefix=$(echo "${opt}" | cut -d '_' -f2-)
sed -i "s;^${optNoPrefix}=.*$;${optNoPrefix}=${val};" "${{ needs.directory.outputs.GENERATOR_ENV }}"
fi
done
- name : Add pull-request environment
if : ${{ github.event_name == 'issue_comment' && github.event.issue.pull_request }}
run : |
2023-10-19 06:19:51 +00:00
sed -i "s;^OSU_B=.*$;OSU_B=${{ github.event.issue.pull_request.html_url }};" "${{ needs.directory.outputs.GENERATOR_ENV }}"
2023-10-05 04:20:07 +00:00
- name : Add comment environment
if : ${{ github.event_name == 'issue_comment' }}
2023-11-10 05:52:03 +00:00
env :
COMMENT_BODY : ${{ github.event.comment.body }}
2023-10-05 04:20:07 +00:00
run : |
# Add comment environment
2023-11-28 13:12:23 +00:00
echo "$COMMENT_BODY" | sed -r 's/\r$//' | grep -E '^\w+=' | while read -r line; do
opt=$(echo "${line}" | cut -d '=' -f1)
2023-10-05 04:20:07 +00:00
sed -i "s;^${opt}=.*$;${line};" "${{ needs.directory.outputs.GENERATOR_ENV }}"
done
- name : Add dispatch environment
if : ${{ github.event_name == 'workflow_dispatch' }}
run : |
sed -i 's;^OSU_B=.*$;OSU_B=${{ inputs.osu-b }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
sed -i 's/^RULESET=.*$/RULESET=${{ inputs.ruleset }}/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
sed -i 's/^GENERATORS=.*$/GENERATORS=${{ inputs.generators }}/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
if [[ '${{ inputs.osu-a }}' != 'latest' ]]; then
sed -i 's;^OSU_A=.*$;OSU_A=${{ inputs.osu-a }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
2021-09-20 17:27:21 +00:00
fi
2023-10-05 04:20:07 +00:00
if [[ '${{ inputs.difficulty-calculator-a }}' != 'latest' ]]; then
sed -i 's;^DIFFICULTY_CALCULATOR_A=.*$;DIFFICULTY_CALCULATOR_A=${{ inputs.difficulty-calculator-a }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
2021-09-20 17:27:21 +00:00
fi
2023-10-05 04:20:07 +00:00
if [[ '${{ inputs.difficulty-calculator-b }}' != 'latest' ]]; then
sed -i 's;^DIFFICULTY_CALCULATOR_B=.*$;DIFFICULTY_CALCULATOR_B=${{ inputs.difficulty-calculator-b }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
2021-09-20 17:27:21 +00:00
fi
2023-10-05 04:20:07 +00:00
if [[ '${{ inputs.score-processor-a }}' != 'latest' ]]; then
sed -i 's;^SCORE_PROCESSOR_A=.*$;SCORE_PROCESSOR_A=${{ inputs.score-processor-a }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
2021-09-20 17:27:21 +00:00
fi
2023-10-05 04:20:07 +00:00
if [[ '${{ inputs.score-processor-b }}' != 'latest' ]]; then
sed -i 's;^SCORE_PROCESSOR_B=.*$;SCORE_PROCESSOR_B=${{ inputs.score-processor-b }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
fi
if [[ '${{ inputs.converts }}' == 'true' ]]; then
sed -i 's/^NO_CONVERTS=.*$/NO_CONVERTS=0/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
else
sed -i 's/^NO_CONVERTS=.*$/NO_CONVERTS=1/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
fi
if [[ '${{ inputs.ranked-only }}' == 'true' ]]; then
sed -i 's/^RANKED_ONLY=.*$/RANKED_ONLY=1/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
2021-09-20 17:27:21 +00:00
else
2023-10-05 04:20:07 +00:00
sed -i 's/^RANKED_ONLY=.*$/RANKED_ONLY=0/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
2021-09-20 17:27:21 +00:00
fi
2023-10-05 04:20:07 +00:00
scores :
name : Setup scores
needs : [ directory, environment ]
2021-09-17 10:47:41 +00:00
runs-on : self-hosted
2021-09-03 08:56:56 +00:00
steps :
2023-10-05 04:20:07 +00:00
- name : Query latest data
id : query
2021-09-03 08:56:56 +00:00
run : |
2023-10-05 04:20:07 +00:00
ruleset=$(cat ${{ needs.directory.outputs.GENERATOR_ENV }} | grep -E '^RULESET=' | cut -d '=' -f2-)
performance_data_name=$(curl -s "https://data.ppy.sh/" | grep "performance_${ruleset}_top_1000\b" | tail -1 | awk -F "'" '{print $2}' | sed 's/\.tar\.bz2//g')
2021-09-03 08:56:56 +00:00
2023-10-05 04:20:07 +00:00
echo "TARGET_DIR=${{ needs.directory.outputs.GENERATOR_DIR }}/sql/${ruleset}" >> "${GITHUB_OUTPUT}"
echo "DATA_NAME=${performance_data_name}" >> "${GITHUB_OUTPUT}"
- name : Restore cache
id : restore-cache
2024-03-28 15:25:03 +00:00
uses : maxnowack/local-cache@720e69c948191660a90aa1cf6a42fc4d2dacdf30 # v2
2023-10-05 04:20:07 +00:00
with :
path : ${{ steps.query.outputs.DATA_NAME }}.tar.bz2
key : ${{ steps.query.outputs.DATA_NAME }}
2021-09-17 18:57:25 +00:00
2023-10-05 04:20:07 +00:00
- name : Download
if : steps.restore-cache.outputs.cache-hit != 'true'
2021-09-03 08:56:56 +00:00
run : |
2023-10-05 04:20:07 +00:00
wget -q -nc "https://data.ppy.sh/${{ steps.query.outputs.DATA_NAME }}.tar.bz2"
2021-09-03 08:56:56 +00:00
2023-10-05 04:20:07 +00:00
- name : Extract
2021-09-21 16:13:47 +00:00
run : |
2023-10-05 04:20:07 +00:00
tar -I lbzip2 -xf "${{ steps.query.outputs.DATA_NAME }}.tar.bz2"
rm -r "${{ steps.query.outputs.TARGET_DIR }}"
mv "${{ steps.query.outputs.DATA_NAME }}" "${{ steps.query.outputs.TARGET_DIR }}"
2021-09-21 16:13:47 +00:00
2023-10-05 04:20:07 +00:00
beatmaps :
name : Setup beatmaps
needs : directory
runs-on : self-hosted
steps :
- name : Query latest data
id : query
run : |
beatmaps_data_name=$(curl -s "https://data.ppy.sh/" | grep "osu_files" | tail -1 | awk -F "'" '{print $2}' | sed 's/\.tar\.bz2//g')
2021-09-03 08:56:56 +00:00
2023-10-05 04:20:07 +00:00
echo "TARGET_DIR=${{ needs.directory.outputs.GENERATOR_DIR }}/beatmaps" >> "${GITHUB_OUTPUT}"
echo "DATA_NAME=${beatmaps_data_name}" >> "${GITHUB_OUTPUT}"
2021-09-03 08:56:56 +00:00
2023-10-05 04:20:07 +00:00
- name : Restore cache
id : restore-cache
2024-03-28 15:25:03 +00:00
uses : maxnowack/local-cache@720e69c948191660a90aa1cf6a42fc4d2dacdf30 # v2
2021-09-03 08:56:56 +00:00
with :
2023-10-05 04:20:07 +00:00
path : ${{ steps.query.outputs.DATA_NAME }}.tar.bz2
key : ${{ steps.query.outputs.DATA_NAME }}
2021-09-03 08:56:56 +00:00
2023-10-05 04:20:07 +00:00
- name : Download
if : steps.restore-cache.outputs.cache-hit != 'true'
2021-09-03 08:56:56 +00:00
run : |
2023-10-05 04:20:07 +00:00
wget -q -nc "https://data.ppy.sh/${{ steps.query.outputs.DATA_NAME }}.tar.bz2"
2021-09-03 08:56:56 +00:00
2023-10-05 04:20:07 +00:00
- name : Extract
2021-09-03 08:56:56 +00:00
run : |
2023-10-05 04:20:07 +00:00
tar -I lbzip2 -xf "${{ steps.query.outputs.DATA_NAME }}.tar.bz2"
rm -r "${{ steps.query.outputs.TARGET_DIR }}"
mv "${{ steps.query.outputs.DATA_NAME }}" "${{ steps.query.outputs.TARGET_DIR }}"
2021-09-03 08:56:56 +00:00
2023-10-05 04:20:07 +00:00
generator :
name : Run generator
needs : [ directory, environment, scores, beatmaps ]
runs-on : self-hosted
timeout-minutes : 720
outputs :
TARGET : ${{ steps.run.outputs.TARGET }}
SPREADSHEET_LINK : ${{ steps.run.outputs.SPREADSHEET_LINK }}
steps :
- name : Run
id : run
2021-09-03 08:56:56 +00:00
run : |
2023-10-05 04:20:07 +00:00
# Add the GitHub token. This needs to be done here because it's unique per-job.
sed -i 's/^GH_TOKEN=.*$/GH_TOKEN=${{ github.token }}/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
cd "${{ needs.directory.outputs.GENERATOR_DIR }}"
docker-compose up --build generator
link=$(docker-compose logs generator -n 10 | grep 'http' | sed -E 's/^.*(http.*)$/\1/')
target=$(cat "${{ needs.directory.outputs.GENERATOR_ENV }}" | grep -E '^OSU_B=' | cut -d '=' -f2-)
echo "TARGET=${target}" >> "${GITHUB_OUTPUT}"
echo "SPREADSHEET_LINK=${link}" >> "${GITHUB_OUTPUT}"
- name : Shutdown
if : ${{ always() }}
2021-09-03 08:56:56 +00:00
run : |
2023-10-05 04:20:07 +00:00
cd "${{ needs.directory.outputs.GENERATOR_DIR }}"
2023-10-19 18:26:12 +00:00
docker-compose down -v
2021-09-03 08:56:56 +00:00
2023-10-17 11:15:00 +00:00
output-cli :
name : Output info
needs : generator
runs-on : ubuntu-latest
steps :
2023-10-05 04:20:07 +00:00
- name : Output info
2021-09-03 08:56:56 +00:00
run : |
2023-10-17 11:15:00 +00:00
echo "Target: ${{ needs.generator.outputs.TARGET }}"
echo "Spreadsheet: ${{ needs.generator.outputs.SPREADSHEET_LINK }}"
2023-10-05 04:20:07 +00:00
2023-10-20 02:34:08 +00:00
cleanup :
name : Cleanup
needs : [ directory, generator ]
2023-10-20 09:58:32 +00:00
if : ${{ always() && needs.directory.result == 'success' }}
2023-10-20 02:34:08 +00:00
runs-on : self-hosted
2023-10-20 02:35:31 +00:00
steps :
- name : Cleanup
run : |
rm -rf "${{ needs.directory.outputs.GENERATOR_DIR }}"
2023-10-20 02:34:08 +00:00
2023-10-05 04:20:07 +00:00
update-comment :
name : Update PR comment
needs : [ create-comment, generator ]
runs-on : ubuntu-latest
2023-10-17 11:36:24 +00:00
if : ${{ always() && needs.create-comment.result == 'success' }}
2023-10-05 04:20:07 +00:00
steps :
- name : Update comment on success
if : ${{ needs.generator.result == 'success' }}
2024-03-28 15:25:03 +00:00
uses : thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0
2023-10-05 04:20:07 +00:00
with :
2023-10-19 18:26:12 +00:00
comment_tag : ${{ env.EXECUTION_ID }}
2023-10-05 04:20:07 +00:00
mode : upsert
create_if_not_exists : false
message : |
Target : ${{ needs.generator.outputs.TARGET }}
Spreadsheet : ${{ needs.generator.outputs.SPREADSHEET_LINK }}
- name : Update comment on failure
if : ${{ needs.generator.result == 'failure' }}
2024-03-28 15:25:03 +00:00
uses : thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0
2023-10-05 04:20:07 +00:00
with :
2023-10-19 18:26:12 +00:00
comment_tag : ${{ env.EXECUTION_ID }}
2023-10-05 04:20:07 +00:00
mode : upsert
create_if_not_exists : false
message : |
2023-10-05 04:32:20 +00:00
Difficulty calculation failed : ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
2023-10-17 11:36:24 +00:00
- name : Update comment on cancellation
if : ${{ needs.generator.result == 'cancelled' }}
2024-03-28 15:25:03 +00:00
uses : thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0
2023-10-17 11:36:24 +00:00
with :
2023-10-19 18:26:12 +00:00
comment_tag : ${{ env.EXECUTION_ID }}
2023-10-17 11:36:24 +00:00
mode : delete
message : '.' # Appears to be required by this action for non-error status code.