diff --git a/.all-contributorsrc b/.all-contributorsrc index 95122bc1fd..9beea4235e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1733,6 +1733,15 @@ "contributions": [ "bug" ] + }, + { + "login": "kjhollen", + "name": "Kate Hollenbach", + "avatar_url": "https://avatars.githubusercontent.com/u/78966?v=4", + "profile": "https://github.com/kjhollen", + "contributions": [ + "bug" + ] } ], "repoType": "github", @@ -1740,4 +1749,4 @@ "skipCi": true, "commitConvention": "angular", "commitType": "docs" -} +} \ No newline at end of file diff --git a/.github/workflows/contributors-png.yml b/.github/workflows/contributors-png.yml new file mode 100644 index 0000000000..040bfe7256 --- /dev/null +++ b/.github/workflows/contributors-png.yml @@ -0,0 +1,40 @@ +name: Generate Contributors PNG + +on: + push: + paths: + - '.all-contributorsrc' + +jobs: + genimage: + if: github.ref == 'refs/heads/main' && github.repository == 'processing/processing4' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: npm install canvas + + - name: Run contributors-png generator + run: node utils/contributors-png.js + + - name: Reset all changes except contributors.png + run: | + git restore --staged . + git add contributors.png + git checkout -- . + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 + with: + commit-message: "Update contributors.png from .all-contributorsrc" + branch: update-contributors-png + title: "chore: update contributors.png from .all-contributorsrc" + body: "This PR updates the contributors.png to reflect changes in .all-contributorsrc" + add-paths: contributors.png + token: ${{ secrets.CONTRIBUTOR_PNG_ACCESS_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e21fce7a40..b41c7d62e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,8 @@ name: Releases on: - release: - types: [published] + push: + tags: + - 'processing-*' jobs: version: @@ -21,12 +22,36 @@ jobs: # Set outputs for use in later jobs or steps echo "revision=$REVISION" >> $GITHUB_OUTPUT echo "version=$VERSION" >> $GITHUB_OUTPUT + + create-draft: + name: Create draft release + runs-on: ubuntu-latest + needs: version + permissions: + contents: write + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + TAG: ${{ github.ref_name }} + VERSION: ${{ needs.version.outputs.version }} + steps: + - name: Create draft release + run: | + if gh release view "$TAG" >/dev/null 2>&1; then + echo "Release for $TAG already exists; leaving it as-is." + else + gh release create "$TAG" \ + --draft \ + --title "Processing $VERSION" \ + --generate-notes + fi + reference: name: Publish Processing Reference to release runs-on: ubuntu-latest permissions: contents: write - needs: version + needs: [version, create-draft] steps: - name: Checkout Website Repository uses: actions/checkout@v4 @@ -42,17 +67,23 @@ jobs: run: npm run build - name: Make reference.zip run: npm run zip + - name: Stage reference asset + env: + VERSION: ${{ needs.version.outputs.version }} + run: | + mkdir -p release-assets + cp reference.zip "release-assets/processing-${VERSION}-reference.zip" - name: Upload reference to release - uses: svenstaro/upload-release-action@v2 + uses: softprops/action-gh-release@v2 with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - asset_name: processing-${{ needs.version.outputs.version }}-reference.zip - file: reference.zip + draft: true + tag_name: ${{ github.ref_name }} + files: release-assets/processing-${{ needs.version.outputs.version }}-reference.zip publish-maven: name: Publish Processing Libraries to Maven Central runs-on: ubuntu-latest - needs: version + needs: [version, release-windows, release-macos, release-linux] steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -78,7 +109,7 @@ jobs: publish-gradle: name: Publish Processing Plugins to Gradle Plugin Portal runs-on: ubuntu-latest - needs: version + needs: [version, release-windows, release-macos, release-linux] steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -97,7 +128,7 @@ jobs: ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }} ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }} - + - name: Publish internal plugins to Gradle Plugin Portal run: ./gradlew -c gradle/plugins/settings.gradle.kts publishPlugins env: @@ -114,7 +145,7 @@ jobs: release-windows: name: (windows/${{ matrix.arch }}) Create Processing Release runs-on: ${{ matrix.os }} - needs: version + needs: [version, create-draft] permissions: contents: write strategy: @@ -154,24 +185,27 @@ jobs: timestamp-rfc3161: http://timestamp.acs.microsoft.com timestamp-digest: SHA256 - - name: Upload portable version - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - asset_name: processing-${{ needs.version.outputs.version }}-windows-${{ matrix.arch }}-portable.zip - file: app/build/compose/binaries/main/Processing-${{ needs.version.outputs.version }}.zip + - name: Stage release assets + shell: bash + env: + VERSION: ${{ needs.version.outputs.version }} + ARCH: ${{ matrix.arch }} + run: | + mkdir -p release-assets + cp "app/build/compose/binaries/main/Processing-${VERSION}.zip" "release-assets/processing-${VERSION}-windows-${ARCH}-portable.zip" + cp "app/build/compose/binaries/main/msi/Processing-${VERSION}.msi" "release-assets/processing-${VERSION}-windows-${ARCH}.msi" - - name: Upload installer - uses: svenstaro/upload-release-action@v2 + - name: Upload release assets + uses: softprops/action-gh-release@v2 with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - asset_name: processing-${{ needs.version.outputs.version }}-windows-${{ matrix.arch }}.msi - file: app/build/compose/binaries/main/msi/Processing-${{ needs.version.outputs.version }}.msi + draft: true + tag_name: ${{ github.ref_name }} + files: release-assets/* release-macos: name: (macOS/${{ matrix.arch }}) Create Processing Release runs-on: macos-latest - needs: version + needs: [version, create-draft] permissions: contents: write strategy: @@ -209,24 +243,26 @@ jobs: ORG_GRADLE_PROJECT_compose.desktop.mac.notarization.password: ${{ secrets.PROCESSING_APP_PASSWORD }} ORG_GRADLE_PROJECT_compose.desktop.mac.notarization.teamID: ${{ secrets.PROCESSING_TEAM_ID }} - - name: Upload portables to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - asset_name: processing-${{ needs.version.outputs.version }}-macos-${{ matrix.arch }}-portable.zip - file: app/build/compose/binaries/main/Processing-${{ needs.version.outputs.version }}.zip + - name: Stage release assets + env: + VERSION: ${{ needs.version.outputs.version }} + ARCH: ${{ matrix.arch }} + run: | + mkdir -p release-assets + cp "app/build/compose/binaries/main/Processing-${VERSION}.zip" "release-assets/processing-${VERSION}-macos-${ARCH}-portable.zip" + cp "app/build/compose/binaries/main/dmg/Processing-${VERSION}.dmg" "release-assets/processing-${VERSION}-macos-${ARCH}.dmg" - - name: Upload installers to release - uses: svenstaro/upload-release-action@v2 + - name: Upload release assets + uses: softprops/action-gh-release@v2 with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - asset_name: processing-${{ needs.version.outputs.version }}-macos-${{ matrix.arch }}.dmg - file: app/build/compose/binaries/main/dmg/Processing-${{ needs.version.outputs.version }}.dmg + draft: true + tag_name: ${{ github.ref_name }} + files: release-assets/* release-linux: name: (linux/${{ matrix.arch }}) Create Processing Release runs-on: ${{ matrix.os }} - needs: version + needs: [version, create-draft] permissions: contents: write strategy: @@ -254,19 +290,22 @@ jobs: ORG_GRADLE_PROJECT_revision: ${{ needs.version.outputs.revision }} ORG_GRADLE_PROJECT_compose.desktop.verbose: true - - name: Upload portable to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - asset_name: processing-${{ needs.version.outputs.version }}-linux-${{ matrix.arch }}-portable.zip - file: app/build/compose/binaries/main/Processing-${{ needs.version.outputs.version }}.zip + - name: Stage release assets + env: + VERSION: ${{ needs.version.outputs.version }} + ARCH: ${{ matrix.arch }} + DEB: ${{ matrix.deb }} + run: | + mkdir -p release-assets + cp "app/build/compose/binaries/main/Processing-${VERSION}.zip" "release-assets/processing-${VERSION}-linux-${ARCH}-portable.zip" + cp "app/build/compose/binaries/main/deb/processing_${VERSION}-1_${DEB}.deb" "release-assets/processing-${VERSION}-linux-${ARCH}.deb" - - name: Upload installer to release - uses: svenstaro/upload-release-action@v2 + - name: Upload release assets + uses: softprops/action-gh-release@v2 with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - asset_name: processing-${{ needs.version.outputs.version }}-linux-${{ matrix.arch }}.deb - file: app/build/compose/binaries/main/deb/processing_${{ needs.version.outputs.version }}-1_${{ matrix.deb }}.deb + draft: true + tag_name: ${{ github.ref_name }} + files: release-assets/* - name: Add artifact uses: actions/upload-artifact@v4 @@ -278,7 +317,7 @@ jobs: release-linux-snap: name: (linux/${{ matrix.arch }}) Create Processing Snap Release runs-on: ${{ matrix.os }} - needs: [version, release-linux] + needs: [version, create-draft, release-linux] permissions: contents: write strategy: @@ -316,12 +355,22 @@ jobs: ORG_GRADLE_PROJECT_snapname: ${{ vars.SNAP_NAME }} ORG_GRADLE_PROJECT_snapconfinement: ${{ vars.SNAP_CONFINEMENT }} + - name: Stage release assets + env: + VERSION: ${{ needs.version.outputs.version }} + ARCH: ${{ matrix.arch }} + DEB: ${{ matrix.deb }} + SNAP_NAME: ${{ vars.SNAP_NAME }} + run: | + mkdir -p release-assets + cp "app/build/compose/binaries/main/${SNAP_NAME}_${VERSION}_${DEB}.snap" "release-assets/processing-${VERSION}-linux-${ARCH}.snap" + - name: Upload snap to release - uses: svenstaro/upload-release-action@v2 + uses: softprops/action-gh-release@v2 with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - asset_name: processing-${{ needs.version.outputs.version }}-linux-${{ matrix.arch }}.snap - file: app/build/compose/binaries/main/${{ vars.SNAP_NAME }}_${{ needs.version.outputs.version }}_${{ matrix.deb }}.snap + draft: true + tag_name: ${{ github.ref_name }} + files: release-assets/processing-${{ needs.version.outputs.version }}-linux-${{ matrix.arch }}.snap - name: Upload snap to Snap Store run: snapcraft upload --release=beta app/build/compose/binaries/main/${{ vars.SNAP_NAME }}_${{ needs.version.outputs.version }}_${{ matrix.deb }}.snap @@ -330,7 +379,7 @@ jobs: release-linux-flatpak: name: (linux/${{ matrix.arch }}) Create Processing Flatpak Release runs-on: ${{ matrix.os }} - needs: [ version, release-linux ] + needs: [version, create-draft, release-linux] container: image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-48 options: --privileged @@ -375,9 +424,42 @@ jobs: cache-key: flatpak-builder-${{ github.sha }} arch: ${{ matrix.farch }} + - name: Stage release assets + env: + VERSION: ${{ needs.version.outputs.version }} + ARCH: ${{ matrix.arch }} + run: | + mkdir -p release-assets + cp processing.flatpak "release-assets/processing-${VERSION}-linux-${ARCH}.flatpak" + - name: Upload Flatpak to release - uses: svenstaro/upload-release-action@v2 + uses: softprops/action-gh-release@v2 with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - asset_name: processing-${{ needs.version.outputs.version }}-linux-${{ matrix.arch }}.flatpak - file: processing.flatpak \ No newline at end of file + draft: true + tag_name: ${{ github.ref_name }} + files: release-assets/processing-${{ needs.version.outputs.version }}-linux-${{ matrix.arch }}.flatpak + + publish-release: + name: Publish release + runs-on: ubuntu-latest + environment: release + needs: + - version + - create-draft + - reference + - publish-maven + - publish-gradle + - release-windows + - release-macos + - release-linux + - release-linux-snap + - release-linux-flatpak + permissions: + contents: write + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + TAG: ${{ github.ref_name }} + steps: + - name: Mark release as published + run: gh release edit "$TAG" --draft=false --latest diff --git a/BUILD.md b/BUILD.md index 1216f2e952..9e1fd7e552 100644 --- a/BUILD.md +++ b/BUILD.md @@ -4,21 +4,25 @@ Great to see you are interested in contributing to Processing. To get started yo ## IntelliJ IDEA (recommended) -First, [download the IntelliJ IDEA Community Edition](https://www.jetbrains.com/idea/download/). Make sure to select the "Community Edition", not "Ultimate". The Community Edition is free and built on open-source software. You may need to scroll down to find the download link. +_**Note:** A paid subscription is **not** required. Everything needed to build and work on Processing is available for free in IntelliJ IDEA._ + +1. [Download IntelliJ IDEA](https://www.jetbrains.com/idea/download/) and install it. +1. Clone the `Processing4` repository locally. +1. Open the cloned repository in **IntelliJ IDEA**. +1. When prompted, select **Trust Project**. (You can preview the project in Safe Mode, but you will not be able to build Processing.) +1. If IntelliJ asks whether to import the Gradle project, select **Load Gradle Project**. +1. Make sure IntelliJ and Gradle are both using **JDK 17**: + * Go to **`File > Project Structure > Project`** + * Set **Project SDK** to **JDK 17** (we recommend **Eclipse Temurin**) + * If needed, choose **Download JDK…** and install **Version 17** + * Then go to **`Settings > Build, Execution, Deployment > Build Tools > Gradle`** + * Set **Gradle JVM** to the same **JDK 17** +1. Wait for Gradle sync to finish. +1. Click the green **Run** button in the top right to build and launch Processing. You can also use this menu to start a debug session. > [!TIP] > If you encounter any issues with this process, Read the [Troubleshooting and Setup Tips for IntelliJ IDEA](#troubleshooting-and-setup-tips-intellij-idea) -1. Clone the Processing4 repository to your machine locally -1. Open the cloned repository in IntelliJ IDEA CE -1. When prompted, select **Trust Project**. You can preview the project in Safe Mode but you won't be able to build Processing. -1. IntelliJ may ask if you want to load Gradle project. If you allow this, make sure you are using JDK version 17. -1. In the main menu, go to File > Project Structure > Project Settings > Project. -1. In the SDK Dropdown option, select a JDK version 17 or Download the jdk -1. Click the green Run Icon in the top right of the window. This is also where you can find the option to debug Processing. -1. Logs can be found in the `Build` or `Debug` pane on the bottom left of the window - - ## VSCode 1. Clone the Processing4 repository to your machine locally 1. Open the cloned repository in VScode @@ -138,23 +142,32 @@ If you’re building Processing using IntelliJ IDEA and something’s not workin ### Use the Correct JDK (temurin-17) -Make sure IntelliJ is using **temurin-17**, not another version. Some users have reported issues with ms-17. +Make sure IntelliJ is using **temurin-17**. Some users have reported issues with other distributions. + +#### 1. Set the Project SDK -1. Go to **File > Project Structure > Project** -2. Set the **Project SDK** to: `temurin-17 java version "17.0.15"` +1. Go to **`File > Project Structure > Project`** +1. Set the **`Project SDK`** to **`temurin-17`** ![JDK Selection](.github/media/troubleshooting-Intellij-setting-djk-version-manually.png) -If it is not already installed, you can download it by: -1. Clicking the SDK input field and then selecting the `Download JDK...` option from the menu -2. Select Version: `17`, Vendor: `Eclipse Temurin (AdoptOpenJDK HotSpot)` +If Temurin 17 is not on the list, you should install it first: + +1. Select the **`Download JDK...`** option from the menu +1. Pick **Version**: **`17`**, **Vendor**: **`Eclipse Temurin (AdoptOpenJDK HotSpot)`** + +JDK Download + +#### 2. Set the Gradle JVM +Make sure Gradle also uses Temurin 17: -![JDK Download](.github/media/troubleshooting-Intellij-download-jdk.png) +1. Go to **`Settings > Build, Execution, Deployment > Build Tools > Gradle`** +1. Set **`Gradle JVM`** to **`temurin-17`** -Now go back to your main window and -1. Click the green Run Icon in the top right of the window. +_Note: the exact path in settings may vary depending on your version of IntelliJ._ +Gradle JVM ### “Duplicate content roots detected” diff --git a/README.md b/README.md index c71c919019..6c19b6923e 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,8 @@ For licensing information about the Processing website see the [processing-websi Copyright (c) 2015-now The Processing Foundation ## Contributors +![Grid of avatars representing contributors to the Processing4 project](contributors.png) + See [CONTRIBUTORS.md](./CONTRIBUTORS.md) for a list of all contributors to the project. This project follows the [all-contributors specification](https://github.com/all-contributors/all-contributors) and the [Emoji Key](https://all-contributors.github.io/emoji-key/) ✨ for contribution types. Detailed instructions on how to add yourself or add contribution emojis to your name are [here](https://github.com/processing/processing4/issues/839). You can also post an issue or comment on a pull request with the text: `@all-contributors please add @YOUR-USERNAME for THINGS` (where `THINGS` is a comma-separated list of entries from the [list of possible contribution types](https://all-contributors.github.io/emoji-key/)) and our nice bot will add you to [CONTRIBUTORS.md](./CONTRIBUTORS.md) automatically! diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4f91e6d98c..0dbd48fdb9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -186,7 +186,7 @@ tasks.register("packageCustomDmg"){ onlyIf { OperatingSystem.current().isMacOsX } group = "compose desktop" - dependsOn(distributable(), "installCreateDmg") + dependsOn("signApp", "installCreateDmg") val packageName = distributable().packageName.get() val dir = distributable().destinationDir.get() @@ -359,12 +359,12 @@ tasks.register("zipDistributable"){ dependsOn("createDistributable", "setExecutablePermissions") group = "compose desktop" - val dir = distributable().destinationDir.get() - val packageName = distributable().packageName.get() + val dir = provider { distributable().destinationDir.get() } + val packageName = provider { distributable().packageName.get() } from(dir){ eachFile{ permissions{ unix("755") } } } archiveBaseName.set(packageName) - destinationDirectory.set(dir.file("../").asFile) + destinationDirectory.set(layout.dir(provider { dir.get().file("../").asFile })) } afterEvaluate{ @@ -390,6 +390,25 @@ afterEvaluate{ } } +val verifySignedMacApp = tasks.register("verifySignedMacApp") { + onlyIf { OperatingSystem.current().isMacOsX } + dependsOn("createDistributable") + group = "compose desktop" + + commandLine( + "codesign", + "-vvv", + "--deep", + "--strict", + layout.buildDirectory.dir("compose/binaries/main/app/Processing.app").get().asFile.absolutePath + ) +} + +afterEvaluate { + tasks.named("notarizeDmg").configure { + dependsOn(verifySignedMacApp) + } +} // LEGACY TASKS // Most of these are shims to be compatible with the old build system @@ -571,9 +590,35 @@ tasks.register("signResources"){ } file(composeResources("Info.plist")).delete() } +} + +/* for mac, perform one final signature of the whole app before submitting + * the app for notarization. + */ +tasks.register("signApp"){ + onlyIf { + OperatingSystem.current().isMacOsX + && + compose.desktop.application.nativeDistributions.macOS.signing.sign.get() + } + + group = "compose desktop" + dependsOn("createDistributable", "setExecutablePermissions") + val packageName = distributable().packageName.get() + val dir = distributable().destinationDir.get() + val app = dir.file("$packageName.app").asFile + commandLine( + "codesign", + "--timestamp", + "--force", + "--deep", + "--options=runtime", + "--sign", "Developer ID Application", + app) } + tasks.register("setExecutablePermissions") { description = "Sets executable permissions on binaries in Processing.app resources" group = "compose desktop" diff --git a/contributors.png b/contributors.png new file mode 100644 index 0000000000..2f252b0b17 Binary files /dev/null and b/contributors.png differ diff --git a/core/src/processing/data/Table.java b/core/src/processing/data/Table.java index ca04dd6ab7..401a4a86ee 100644 --- a/core/src/processing/data/Table.java +++ b/core/src/processing/data/Table.java @@ -224,22 +224,27 @@ public Table(ResultSet rs) { int type = rsmd.getColumnType(col + 1); switch (type) { // TODO these aren't tested. nor are they complete. - case Types.INTEGER: - case Types.TINYINT: - case Types.SMALLINT: - setColumnType(col, INT); - break; - case Types.BIGINT: - setColumnType(col, LONG); - break; - case Types.FLOAT: - setColumnType(col, FLOAT); - break; - case Types.DECIMAL: - case Types.DOUBLE: - case Types.REAL: - setColumnType(col, DOUBLE); - break; + case Types.VARCHAR: + case Types.CHAR: + case Types.BLOB: + setColumnType(col, STRING); + break; + case Types.INTEGER: + case Types.TINYINT: + case Types.SMALLINT: + setColumnType(col, INT); + break; + case Types.BIGINT: + setColumnType(col, LONG); + break; + case Types.FLOAT: + setColumnType(col, FLOAT); + break; + case Types.DECIMAL: + case Types.DOUBLE: + case Types.REAL: + setColumnType(col, DOUBLE); + break; } } diff --git a/core/test/processing/core/PGraphicsTests.java b/core/test/processing/core/PGraphicsTests.java new file mode 100644 index 0000000000..8d30b8fb71 --- /dev/null +++ b/core/test/processing/core/PGraphicsTests.java @@ -0,0 +1,19 @@ +package processing.core; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import processing.core.PGraphics; + +public class PGraphicsTests { + + @Test + public void testCanvasSizeAfterSetSize() { + // Create a PGraphics object and set its size + PGraphics pg = new PGraphics(); + pg.setSize(200, 150); + + // Assert that both width and height are correctly initialized + assertEquals(200, pg.width); + assertEquals(150, pg.height); + } +} \ No newline at end of file diff --git a/java/gradle/README.md b/java/gradle/README.md index b827972769..d49d609764 100644 --- a/java/gradle/README.md +++ b/java/gradle/README.md @@ -18,7 +18,9 @@ more advanced workflows. ## What is Gradle Gradle is a build tool commonly used in the Java ecosystem. It is responsible for tasks like compiling code, managing -dependencies, and running applications. You do not need to learn Gradle to use Processing in the P +dependencies, and running applications. You do not need to learn Gradle to use Processing in the PDE, but if you feel +ready to take your sketches beyond the PDE and into development environments such as IntelliJ or Eclipse, this plugin +helps bridge the gap between Processing and standard Java workflows. ## Usage @@ -110,4 +112,4 @@ plugins { `settings.gradle.kts` - create the file but leave blank -Then run all sketches at once with `gradle sketch` \ No newline at end of file +Then run all sketches at once with `gradle sketch` diff --git a/utils/contributors-png.js b/utils/contributors-png.js new file mode 100644 index 0000000000..2686f1685f --- /dev/null +++ b/utils/contributors-png.js @@ -0,0 +1,61 @@ +const { createCanvas, loadImage } = require('canvas'); +const fs = require('fs'); + +const data = fs.readFileSync('.all-contributorsrc', 'utf-8'); +const parsed = JSON.parse(data); +const contributors = parsed.contributors; + +const AVATAR_SIZE = 50; +const GAP = 4; +const COLS = 40; +const ROWS = Math.ceil(contributors.length / COLS); + +const width = COLS * AVATAR_SIZE + (COLS - 1) * GAP; +const height = ROWS * AVATAR_SIZE + (ROWS - 1) * GAP; + +const canvas = createCanvas(width, height); +const ctx = canvas.getContext('2d'); + +async function loadAvatar(url) { + try { + const res = await fetch(url); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + + const buffer = Buffer.from(await res.arrayBuffer()); + return await loadImage(buffer); + } catch (err) { + return null; + } +} + +(async () => { + for (let i = 0; i < contributors.length; i++) { + const c = contributors[i]; + + const col = i % COLS; + const row = Math.floor(i / COLS); + + const x = col * (AVATAR_SIZE + GAP); + const y = row * (AVATAR_SIZE + GAP); + + const img = await loadAvatar(c.avatar_url); + + ctx.save(); + ctx.beginPath(); + ctx.arc( + x + AVATAR_SIZE / 2, + y + AVATAR_SIZE / 2, + AVATAR_SIZE / 2, + 0, + Math.PI * 2 + ); + ctx.clip(); + + if (img) { + ctx.drawImage(img, x, y, AVATAR_SIZE, AVATAR_SIZE); + } + ctx.restore(); + } + + fs.writeFileSync('contributors.png', canvas.toBuffer('image/png')); +})();