diff --git a/.breakage/Project.toml b/.breakage/Project.toml new file mode 100644 index 000000000..a65fa1cec --- /dev/null +++ b/.breakage/Project.toml @@ -0,0 +1,3 @@ +[deps] +GitHub = "bc5e4493-9b4d-5f90-b8aa-2b2bcaad7a26" +PkgDeps = "839e9fc8-855b-5b3c-a3b7-2833d3dd1f59" diff --git a/.breakage/get_jso_users.jl b/.breakage/get_jso_users.jl new file mode 100644 index 000000000..5a9f49789 --- /dev/null +++ b/.breakage/get_jso_users.jl @@ -0,0 +1,18 @@ +import GitHub, PkgDeps # both export users() + +length(ARGS) >= 1 || error("specify at least one JSO package as argument") + +jso_repos, _ = GitHub.repos("JuliaSmoothOptimizers") +jso_names = [splitext(x.name)[1] for x ∈ jso_repos] + +name = splitext(ARGS[1])[1] +name ∈ jso_names || error("argument should be one of ", jso_names) + +dependents = String[] +try + global dependents = filter(x -> x ∈ jso_names, PkgDeps.users(name)) +catch e + # package not registered; don't insert into dependents +end + +println(dependents) diff --git a/.github/workflows/Breakage.yml b/.github/workflows/Breakage.yml index aebe38514..c80819806 100644 --- a/.github/workflows/Breakage.yml +++ b/.github/workflows/Breakage.yml @@ -7,21 +7,53 @@ on: pull_request: jobs: + # Build dynamically the matrix on which the "break" job will run. + # The matrix contains the packages that depend on ${{ env.pkg }}. + # Job "setup_matrix" outputs variable "matrix", which is in turn + # the output of the "getmatrix" step. + # The contents of "matrix" is a JSON description of a matrix used + # in the next step. It has the form + # { + # "pkg": [ + # "PROPACK", + # "LLSModels", + # "FletcherPenaltySolver" + # ] + # } + setup_matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.getmatrix.outputs.matrix }} + env: + pkg: ${{ github.event.repository.name }} + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: 1 + arch: x64 + - id: getmatrix + run: | + julia -e 'using Pkg; Pkg.Registry.add(RegistrySpec(url = "https://github.com/JuliaRegistries/General.git"))' + julia --project=.breakage -e 'using Pkg; Pkg.update(); Pkg.instantiate()' + pkgs=$(julia --project=.breakage .breakage/get_jso_users.jl ${{ env.pkg }}) + vs='["latest", "stable"]' + # Check if pkgs is empty, and set it to a JSON array if necessary + if [[ -z "$pkgs" || "$pkgs" == "String[]" ]]; then + echo "No packages found; exiting successfully." + exit 0 + fi + vs='["latest", "stable"]' + matrix=$(jq -cn --argjson deps "$pkgs" --argjson vers "$vs" '{pkg: $deps, pkgversion: $vers}') # don't escape quotes like many posts suggest + echo "matrix=$matrix" >> "$GITHUB_OUTPUT" + break: + needs: setup_matrix + if: needs.setup_matrix.result == 'success' && needs.setup_matrix.outputs.matrix != '' runs-on: ubuntu-latest strategy: fail-fast: false - matrix: - pkg: [ - "JuliaSmoothOptimizers/CaNNOLeS.jl", - "JuliaSmoothOptimizers/DCISolver.jl", - "JuliaSmoothOptimizers/FletcherPenaltySolver.jl", - "JuliaSmoothOptimizers/JSOSolvers.jl", - "JuliaSmoothOptimizers/LLSModels.jl", - "JuliaSmoothOptimizers/Percival.jl", - "JuliaSmoothOptimizers/RipQP.jl" - ] - pkgversion: [latest, stable] + matrix: ${{ fromJSON(needs.setup_matrix.outputs.matrix) }} steps: - uses: actions/checkout@v4 @@ -29,9 +61,9 @@ jobs: # Install Julia - uses: julia-actions/setup-julia@v2 with: - version: '1' + version: 1 arch: x64 - - uses: actions/cache@v3 + - uses: actions/cache@v4 env: cache-name: cache-artifacts with: @@ -46,15 +78,13 @@ jobs: # Breakage test - name: 'Breakage of ${{ matrix.pkg }}, ${{ matrix.pkgversion }} version' env: - URL: ${{ matrix.pkg }} + PKG: ${{ matrix.pkg }} VERSION: ${{ matrix.pkgversion }} run: | set -v - mkdir -p ./pr - echo "${{ github.event.number }}" > ./pr/NR - git clone https://github.com/$URL - export PKG=$(echo $URL | cut -f2 -d/) - cd $PKG + mkdir -p ./breakage + git clone https://github.com/JuliaSmoothOptimizers/$PKG.jl.git + cd $PKG.jl if [ $VERSION == "stable" ]; then TAG=$(git tag -l "v*" --sort=-creatordate | head -n1) if [ -z "$TAG" ]; then @@ -69,12 +99,15 @@ jobs: julia -e 'using Pkg; PKG, TAG, VERSION = ENV["PKG"], ENV["TAG"], ENV["VERSION"] joburl = joinpath(ENV["GITHUB_SERVER_URL"], ENV["GITHUB_REPOSITORY"], "actions/runs", ENV["GITHUB_RUN_ID"]) - open("../pr/$PKG-$VERSION", "w") do io + open("../breakage/breakage-$PKG-$VERSION", "w") do io try - TAG == "no_tag" && error("Not tag for $VERSION") + TAG == "no_tag" && error("No tag for $VERSION") pkg"activate ."; pkg"instantiate"; pkg"dev ../"; + if TAG == "latest" + global TAG = chomp(read(`git rev-parse --short HEAD`, String)) + end pkg"build"; pkg"test"; @@ -87,8 +120,8 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: pr - path: pr/ + name: breakage-${{ matrix.pkg }}-${{ matrix.pkgversion }} + path: breakage/breakage-* upload: needs: break @@ -98,21 +131,20 @@ jobs: - uses: actions/download-artifact@v4 with: - name: pr - path: pr/ + path: breakage + pattern: breakage-* + merge-multiple: true - - run: ls + - run: ls -R - run: | - cd pr - echo "| Package name | latest | stable |" > MSG - echo "|--|--|--|" >> MSG + cd breakage + echo "| Package name | latest | stable |" > summary.md + echo "|--|--|--|" >> summary.md count=0 - for file in * + for file in breakage-* do - [ "$file" == "NR" ] && continue - [ "$file" == "MSG" ] && continue if [ $count == "0" ]; then - name=$(echo $file | cut -f1 -d-) + name=$(echo $file | cut -f2 -d-) echo -n "| $name | " else echo -n "| " @@ -125,9 +157,45 @@ jobs: echo " |" count=0 fi - done >> MSG + done >> summary.md - - uses: actions/upload-artifact@v4 + - name: PR comment with file + uses: actions/github-script@v6 with: - name: pr - path: pr/ + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // Import file content from summary.md + const fs = require('fs') + const filePath = 'breakage/summary.md' + const msg = fs.readFileSync(filePath, 'utf8') + + // Get the current PR number from context + const prNumber = context.payload.pull_request.number + + // Fetch existing comments on the PR + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber + }) + + // Find a previous comment by the bot to update + const botComment = comments.find(comment => comment.user.id === 41898282) + + if (botComment) { + // Update the existing comment + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: msg + }) + } else { + // Create a new comment + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: msg + }) + } diff --git a/.github/workflows/CommentPR.yml b/.github/workflows/CommentPR.yml deleted file mode 100644 index 043113f74..000000000 --- a/.github/workflows/CommentPR.yml +++ /dev/null @@ -1,74 +0,0 @@ -# Ref: https://securitylab.github.com/research/github-actions-preventing-pwn-requests -name: Comment on the pull request - -# read-write repo token -# access to secrets -on: - workflow_run: - workflows: ["Breakage"] - types: - - completed - -jobs: - upload: - runs-on: ubuntu-latest - if: > - github.event.workflow_run.event == 'pull_request' && - github.event.workflow_run.conclusion == 'success' - steps: - - name: 'Download artifact' - uses: actions/github-script@v3.1.0 - with: - script: | - var artifacts = await github.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: ${{github.event.workflow_run.id }}, - }); - var matchArtifact = artifacts.data.artifacts.filter((artifact) => { - return artifact.name == "pr" - })[0]; - var download = await github.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: matchArtifact.id, - archive_format: 'zip', - }); - var fs = require('fs'); - fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - - run: unzip pr.zip - - - name: 'Comment on PR' - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - var fs = require('fs') - var issue_number = Number(fs.readFileSync('./NR')) - var msg = fs.readFileSync('./MSG', 'utf8') - - // Get the existing comments. - const {data: comments} = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue_number - }) - - // Find any comment already made by the bot. - const botComment = comments.find(comment => comment.user.id === 41898282) - - if (botComment) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: botComment.id, - body: msg - }) - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue_number, - body: msg - }) - } diff --git a/copier-answers.jso.yml b/copier-answers.jso.yml new file mode 100644 index 000000000..d8d4a621d --- /dev/null +++ b/copier-answers.jso.yml @@ -0,0 +1,8 @@ +PackageName: "Krylov" +PackageOwner: "JuliaSmoothOptimizers" +PackageUUID: "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" +_src_path: "https://github.com/JuliaSmoothOptimizers/JSOBestieTemplate.jl" +_commit: "v0.13.0" +AddBreakage: true +AddBenchmark: false +AddBenchmarkCI: true