diff --git a/.github/PULL_REQUEST_TEMPLATE/new_icon.md b/.github/PULL_REQUEST_TEMPLATE/new_icon.md index bcc4f6a72..9e35dc41e 100644 --- a/.github/PULL_REQUEST_TEMPLATE/new_icon.md +++ b/.github/PULL_REQUEST_TEMPLATE/new_icon.md @@ -3,7 +3,7 @@ - [ ] PR does not match another non-stale PR currently opened - [ ] PR name matches the format *new icon: Icon name (versions separated by comma)*. More details [here](https://github.com/devicons/devicon/wiki/Overview-on-Submitting-Icons) -- [ ] PR's base is the `develop` branch. +- [ ] PR's base is the `master` branch. - [ ] Your icons are inside a folder as seen [here](https://github.com/devicons/devicon/wiki/Organizing-SVGs) - [ ] SVG matches the standards laid out [here](https://github.com/devicons/devicon/wiki/SVG-Standards) - [ ] A new object is added in the `devicon.json` file at the correct alphabetic position as seen [here](https://github.com/devicons/devicon/wiki/Updating-%60devicon.json%60) diff --git a/.github/scripts/in_develop_labeler.py b/.github/scripts/in_develop_labeler.py deleted file mode 100644 index 3d329c994..000000000 --- a/.github/scripts/in_develop_labeler.py +++ /dev/null @@ -1,24 +0,0 @@ -import re -from build_assets import arg_getters, api_handler - -def main(): - args = arg_getters.get_in_develop_labeler_args() - try: - #get pr body - pr_body = api_handler.get_pr_by_number(args.token, args.pr_num)["body"] - - # find the issue closing line - print(pr_body.split("\n")) - issue_line = [line for line in pr_body.split("\n") if line.startswith("**This PR closes")][0] - - print("Issue Line is " + issue_line) - issue_pattern = re.compile(r"\d+") - issues_numbers = issue_pattern.findall(issue_line) - print("Labelling issues: " + str(issues_numbers)) - api_handler.label_issues(args.token, issues_numbers, ["in-develop"]) - except IndexError: # if can't find the issue line - print("The PR body doesn't contain `**This PR closes` keywords. Ending workflow.") - return - -if __name__ == "__main__": - main() diff --git a/.github/workflows/auto_build_merge.yml b/.github/workflows/auto_build_merge.yml new file mode 100644 index 000000000..3788d5821 --- /dev/null +++ b/.github/workflows/auto_build_merge.yml @@ -0,0 +1,188 @@ +name: Auto Build and Merge +on: + pull_request_review: + types: [submitted] + +jobs: + auto-build-merge: + name: Auto Build and Merge + runs-on: ubuntu-latest + if: github.event.review.state == 'approved' && github.event.pull_request.base.ref == 'master' + + steps: + - name: Check if PR modifies icon files + id: check-files + uses: actions/github-script@v7 + with: + script: | + const { data: files } = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number + }); + + const hasIconChanges = files.some(file => + file.filename.startsWith('icons/') || file.filename === 'devicon.json' + ); + + console.log('Has icon changes:', hasIconChanges); + core.setOutput('has_icon_changes', hasIconChanges); + + if (!hasIconChanges) { + console.log('PR does not modify icon files, skipping build'); + } + + return hasIconChanges; + + - name: Check approval count + if: steps.check-files.outputs.has_icon_changes == 'true' + id: check-approvals + uses: actions/github-script@v7 + with: + script: | + const { data: reviews } = await github.rest.pulls.listReviews({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number + }); + + // Get unique approvers (latest review per user) + const reviewsByUser = {}; + for (const review of reviews) { + if (!reviewsByUser[review.user.login] || + new Date(review.submitted_at) > new Date(reviewsByUser[review.user.login].submitted_at)) { + reviewsByUser[review.user.login] = review; + } + } + + const approvals = Object.values(reviewsByUser).filter(r => r.state === 'APPROVED'); + const approvalCount = approvals.length; + + console.log('Approval count:', approvalCount); + core.setOutput('approval_count', approvalCount); + core.setOutput('approvers', JSON.stringify(approvals.map(r => r.user.login))); + + if (approvalCount < 1) { + console.log('Less than 1 approval, skipping build'); + return false; + } + + return true; + + - name: Checkout PR branch + if: steps.check-approvals.outputs.approval_count >= 1 + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Python 3.10 + if: steps.check-approvals.outputs.approval_count >= 1 + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Setup Node.js + if: steps.check-approvals.outputs.approval_count >= 1 + uses: actions/setup-node@v4 + with: + node-version: '16' + + - name: Install Python dependencies + if: steps.check-approvals.outputs.approval_count >= 1 + run: | + python -m pip install --upgrade pip + pip install -r ./.github/scripts/requirements.txt + + - name: Install Node dependencies + if: steps.check-approvals.outputs.approval_count >= 1 + run: npm install + + - name: Run Icomoon build + if: steps.check-approvals.outputs.approval_count >= 1 + id: build + continue-on-error: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + python ./.github/scripts/icomoon_build.py \ + ./.github/scripts/build_assets/geckodriver-v0.32.2-linux64/geckodriver \ + ./icomoon.json \ + ./devicon.json \ + ./icons \ + ./ \ + $GITHUB_TOKEN \ + --headless + + - name: Build CSS + if: steps.build.outcome == 'success' + id: build-css + continue-on-error: true + run: npm run build-css + + - name: Handle build failure + if: steps.build.outcome == 'failure' || steps.build-css.outcome == 'failure' + uses: actions/github-script@v7 + with: + script: | + const approvers = JSON.parse('${{ steps.check-approvals.outputs.approvers }}'); + const prAuthor = context.payload.pull_request.user.login; + const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + const mentions = ['@' + prAuthor, ...approvers.map(a => '@' + a)].join(' '); + + const commentBody = `${mentions} + +The Icomoon build has failed for this PR. Please review the errors and fix them. + +**Workflow run:** ${runUrl} + +The build must pass before this PR can be merged.`; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: commentBody + }); + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: ['build-failed'] + }); + + core.setFailed('Build failed'); + + - name: Commit build artifacts + if: steps.build.outcome == 'success' && steps.build-css.outcome == 'success' + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git add -A + if ! git diff --staged --quiet; then + git commit -m "build: generate font files and CSS + +🤖 Generated with [Claude Code](https://claude.com/claude-code) + +Co-Authored-By: Claude " + git push + else + echo "No changes to commit" + fi + + - name: Merge PR + if: steps.build.outcome == 'success' && steps.build-css.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + await github.rest.pulls.merge({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + merge_method: 'squash' + }); + + console.log('PR merged successfully'); diff --git a/.github/workflows/build_icons.yml b/.github/workflows/build_icons.yml index e5741d0ce..f67384286 100644 --- a/.github/workflows/build_icons.yml +++ b/.github/workflows/build_icons.yml @@ -84,6 +84,7 @@ jobs: Adios, Build Bot :sunglasses: with: + base: master branch: 'bot/build-result' commit-message: 'Built new icons, icomoon.json and devicon.css' title: 'bot:build new icons, icomoon.json and devicon.css' diff --git a/.github/workflows/check_icon_pr.yml b/.github/workflows/check_icon_pr.yml index 27e5cae47..9ce102d62 100644 --- a/.github/workflows/check_icon_pr.yml +++ b/.github/workflows/check_icon_pr.yml @@ -1,32 +1,33 @@ name: Check Icon PR -on: pull_request +on: + pull_request: + paths: + - 'icons/**' + - 'devicon.json' jobs: check: name: Check the `devicon.json` and the SVGs' quality runs-on: ubuntu-latest - if: startsWith(github.event.pull_request.title, 'new icon') || startsWith(github.event.pull_request.title, 'update icon') # only checks icon PR steps: - uses: actions/checkout@v4 - - name: Check if PR is develop - if: ${{ github.base_ref != 'develop' }} + - name: Check if PR targets master + if: ${{ github.base_ref != 'master' }} run: | - echo -e "The PR's base branch is \`${{ github.base_ref }}\`, but should be \`develop\`\nPlease change the PR so that it's based on, and merged into \`develop\`" > ./err_messages.txt - echo "wrong_branch=true" >> $GITHUB_ENV + echo "Error: This PR targets '${{ github.base_ref }}' but should target 'master'" + echo "Please change the PR so that it's based on, and merged into 'master'" + exit 1 - uses: actions/setup-python@v5 - if: ${{ !env.wrong_branch }} with: python-version: 3.8 - name: Install dependencies - if: ${{ !env.wrong_branch }} run: | python -m pip install --upgrade pip pip install -r ./.github/scripts/requirements.txt - name: Run the check_svg script - if: ${{ !env.wrong_branch }} env: PR_TITLE: ${{ github.event.pull_request.title }} run: python ./.github/scripts/check_icon_pr.py "$PR_TITLE" ./icons ./devicon.json diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4dbf64031..690492c3c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,9 +2,9 @@ name: "Code Scanning - Action" on: push: - branches: [master, develop] + branches: [master] pull_request: - branches: [master, develop] + branches: [master] schedule: - cron: 30 1 * * 0 # Runs every Sunday 1:30 am UTC diff --git a/.github/workflows/in_develop_labeler.yml b/.github/workflows/in_develop_labeler.yml deleted file mode 100644 index 7cf67f1c0..000000000 --- a/.github/workflows/in_develop_labeler.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Label Issue In Develop -on: - workflow_run: - workflows: ['On Develop PR Merge'] - types: - - completed -jobs: - on-failure: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'failure' }} - steps: - - run: echo "First workflow was a failure" - label_preflight: - name: Label Issue In Develop - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} - steps: - - uses: actions/checkout@v4 - - - name: Setup Python v3.8 - uses: actions/setup-python@v5 - with: - python-version: 3.8 - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r ./.github/scripts/requirements.txt - - - name: Download workflow artifact - uses: dawidd6/action-download-artifact@v7 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - workflow: peek_icons.yml - run_id: ${{ github.event.workflow_run.id }} - - - name: Read the pr_num file - id: pr_num_reader - uses: juliangruber/read-file-action@v1.0.0 - with: - path: ./pr_num/pr_num.txt - - - name: Run in_develop_labeler.py - env: - TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUM: ${{ steps.pr_num_reader.outputs.content }} - run: python ./.github/scripts/in_develop_labeler.py $TOKEN "$PR_NUM" diff --git a/.github/workflows/in_develop_labeler_preflight.yml b/.github/workflows/in_develop_labeler_preflight.yml deleted file mode 100644 index b2151692c..000000000 --- a/.github/workflows/in_develop_labeler_preflight.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: On Develop PR Merge -on: - pull_request: - types: [closed] - branches: [develop] -jobs: - save_pr_num_in_artifact: - name: Preflight Label Issue In Develop - runs-on: ubuntu-latest - if: github.event.pull_request.merged == true - steps: - - uses: actions/checkout@v4 - - - name: Save the PR number in an artifact - shell: bash - env: - PR_NUM: ${{ github.event.number }} - run: echo $PR_NUM > pr_num.txt - - - name: Upload the PR number - uses: actions/upload-artifact@v4 - with: - name: pr_num - path: ./pr_num.txt diff --git a/.github/workflows/peek_icons.yml b/.github/workflows/peek_icons.yml index a337d5543..495222432 100644 --- a/.github/workflows/peek_icons.yml +++ b/.github/workflows/peek_icons.yml @@ -1,14 +1,16 @@ name: Peek Icons on: pull_request: - types: [labeled] + types: [opened, synchronize, reopened] + paths: + - 'icons/**' + - 'devicon.json' jobs: peek: # four outcomes: successful check and upload, # unsuccessful check (fail due to user), # fail due to system, skipped name: Peek Icons - if: github.event.label.name == 'bot:peek' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/version_and_release.yml b/.github/workflows/version_and_release.yml new file mode 100644 index 000000000..299dd4d37 --- /dev/null +++ b/.github/workflows/version_and_release.yml @@ -0,0 +1,101 @@ +name: Version and Release +on: + push: + branches: [master] + paths: + - 'icons/**' + - 'devicon.json' + +permissions: + contents: write + +jobs: + version-and-release: + name: Bump Version and Create Release + runs-on: ubuntu-latest + steps: + - name: Skip if version bump or build commit + id: check-skip + run: | + MSG="${{ github.event.head_commit.message }}" + if [[ "$MSG" == build:* ]] || [[ "$MSG" == chore:\ bump\ version* ]]; then + echo "skip=true" >> $GITHUB_OUTPUT + else + echo "skip=false" >> $GITHUB_OUTPUT + fi + + - name: Checkout master + if: steps.check-skip.outputs.skip == 'false' + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + - name: Get merged PR number and labels + if: steps.check-skip.outputs.skip == 'false' + id: pr-info + uses: actions/github-script@v7 + with: + script: | + const msg = context.payload.head_commit.message; + const match = msg.match(/\(#(\d+)\)/); + if (!match) { + console.log('No PR number found in commit message, defaulting to patch'); + core.setOutput('bump', 'patch'); + return; + } + + const prNumber = parseInt(match[1]); + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + + const labels = pr.labels.map(l => l.name); + console.log('PR labels:', labels); + + let bump = 'patch'; + if (labels.includes('release: major')) bump = 'major'; + else if (labels.includes('release: minor')) bump = 'minor'; + + console.log('Version bump type:', bump); + core.setOutput('bump', bump); + core.setOutput('pr_number', prNumber); + + - name: Setup Node.js + if: steps.check-skip.outputs.skip == 'false' + uses: actions/setup-node@v4 + with: + node-version: '16' + + - name: Configure git + if: steps.check-skip.outputs.skip == 'false' + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + + - name: Bump version + if: steps.check-skip.outputs.skip == 'false' + id: bump + run: | + BUMP="${{ steps.pr-info.outputs.bump }}" + npm version $BUMP -m "chore: bump version to %s" + VERSION=$(node -p "require('./package.json').version") + echo "version=$VERSION" >> $GITHUB_OUTPUT + git push --follow-tags + + - name: Create GitHub release + if: steps.check-skip.outputs.skip == 'false' + uses: actions/github-script@v7 + with: + script: | + const version = '${{ steps.bump.outputs.version }}'; + await github.rest.repos.createRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + tag_name: `v${version}`, + name: `Release v${version}`, + generate_release_notes: true + }); + console.log(`Release v${version} created`); diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4efc27ec7..772743dbe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -75,7 +75,7 @@ First of all, thanks for taking the time to contribute! This project can only gr
  • Create the SVGs for each SVG versions that you have. Follow the convention listed.
  • Put the SVGs of each Icon into its own folders in /icons
  • Update the devicon.json to include the new Icon
  • -
  • Create a separated pull request (PR) towards the develop branch for each Icon.
  • +
  • Create a separated pull request (PR) towards the master branch for each Icon.
  • Fill out the info as stated in the PR template.
  • Include the name of the Icon in the pull request title in this format: new icon: Icon name (versions)
  • Optional: Reference the issues regarding the new icon and label the PR `feature:icon`.
  • @@ -344,7 +344,7 @@ As an example, let's assume you have created the SVGs for Redhat and Amazon Web Create a new commit to fix the SVGs.
  • - Open a pull request based on the `develop` branch. + Open a pull request based on the `master` branch.
  • IMPORTANT: name the pull request update icon: icon-name (versions). Basically, follow the Overview on Submitting Icon but replace the new with update in name of request with the above. @@ -361,19 +361,19 @@ As an example, let's assume you have created the SVGs for Redhat and Amazon Web You don't have to be in a team to contribute!

    - The branches master and develop are protected branches and only members - with corresponding permissions (see teams below) are able to push changes to them. + The master branch is the protected branch and only members + with corresponding permissions (see teams below) are able to push changes to it. Additional branches must follow the pattern username/feature/description. The /feature/ indicates that a change is made to existing code (regardless - if it's a fix, refactor or actual feature). The naming convention is based on the gitflow-workflow. + if it's a fix, refactor or actual feature).

    For organisational purposes we introduced teams with permissions and responsibilities:

    Supporter (@devicons/supporter)
    Members of this team are responsible for reviewing pull request (auto assigned), managing issues and preparing the upcoming release.
    - Supporters have Write access to the repository (allowing them to create own branches) - and are allowed to push changes to the develop branch (pull request and status checks required). + Supporters have Write access to the repository (allowing them to create own branches) + and are allowed to push changes to the master branch (pull request and status checks required).
    Maintainer (@devicons/maintainer)
    @@ -470,29 +470,25 @@ We are running a Discord server. You can go here to talk, discuss, and more with
    Release preparation and execution
    1. Define the next release version number based on the conventions
    2. -
    3. Checkout development as draft-release branch
    4. -
    5. Bump the package version using npm version vMAJOR.MINOR.PATCH -m "bump npm version to vMAJOR.MINOR.PATCH" (see #487)
    6. +
    7. Checkout master as draft-release branch: git checkout master && git checkout -b draft-release
    8. +
    9. Bump the package version using npm version vMAJOR.MINOR.PATCH -m "bump npm version to vMAJOR.MINOR.PATCH"
    10. Push the branch draft-release
    11. Manually trigger the workflow build_icons.yml (which has a workflow_dispatch event trigger) and select the branch draft-release as target branch. This will build a font version of all icons using icomoon and automatically creates a pull request to merge the build result back into draft-release
    12. -
    13. Review and approve the auto-create pull request created by the action of the step above
    14. -
    15. Create a pull request towards development. Mention the release number in the pull request title (like "Build preparation for release vMAJOR.MINOR.PATCH). -
        -
      • - Add information about all new icons, fixes, features and enhancements in the description of the pull request. -
      • +
      • Review, approve and merge the auto-created pull request
      • +
      • Create a pull request from draft-release towards master. Mention the release number in the pull request title (like "Release vMAJOR.MINOR.PATCH"). +
        • - Take the PRs/commits as a guideline. It's also a good idea to mention and thank all contributions who participated in the release (take description of #504 as an example). + Add information about all new icons, fixes, features and enhancements in the description of the pull request.
        • - We now have a script that will do this for us. Check the `build-bot`'s PR message in the last step. There should be a section where it displays the features that have been added to the release. You can copy the markdown there and use it for the release message. + We have a script that will do this for you. Check the `build-bot`'s PR message in the previous step. There should be a section where it displays the features that have been added to the release. You can copy the markdown there and use it for the release message.
      • -
      • Wait for review and approval of the pull request (you can perform a squash-merge)
      • -
      • Once merged create a pull request with BASE master and HEAD development. Copy the description of the earlier pull request.
      • -
      • Since it was already approved in the 'development' stage a maintainer is allowed to merge it (DON'T perform a squash-merge).
      • +
      • Since it was already reviewed, a maintainer can merge it (DON'T perform a squash-merge).
      • Create a new release using the format "Release vMAJOR.MINOR.PATCH" as tag and release title. Use the earlier created description as description of the release.
      • -
      • Publishing the release will trigger the npm_publish.yml workflow which will execute a npm publish leading to a updated npm package (vMAJOR.MINOR.PATCH).
      • +
      • Publishing the release will trigger the npm_publish.yml workflow which will execute a npm publish leading to an updated npm package (vMAJOR.MINOR.PATCH).
      • +
      • If icons fail to appear on the website, clear the CDN cache.

    Recommended resources and tools