Skip to content

Commit

Permalink
Merge pull request #4 from iodigital-com/gradle-plugin-2
Browse files Browse the repository at this point in the history
Add gradle plugin, improve mode handling
  • Loading branch information
crysxd authored Oct 31, 2024
2 parents ada9e7f + 58d9c10 commit 644905c
Show file tree
Hide file tree
Showing 67 changed files with 1,529 additions and 550 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@ jobs:

- name: Build distribution
id: build
run: gradle clean build -PAzureBuildNumber="${{ github.run_id }}"
run: ./gradlew clean build -PAzureBuildNumber="${{ github.run_id }}"

- name: Publish to Maven Central
run: ./gradlew publish
env:
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_mavenCentralPassword }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKey }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKeyPassword }}
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ORG_GRADLE_PROJECT_mavenCentralUsername }}

- name: Create tag
uses: tvdias/[email protected]
Expand Down
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ captures
.cxx
local.properties
xcuserdata
figma-exporter/sampleconfig.json
figma-exporter/.figmatoken
sampleconfig.json
.figmatoken
/sample_output/
2 changes: 1 addition & 1 deletion .run/Run sample.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<option name="taskNames">
<list>
<option value="jvmRun" />
<option value="--args='-c sampleconfig.json'" />
<option value="--args='-c ../samples/config.json -i'" />
</list>
</option>
<option name="vmOptions" />
Expand Down
184 changes: 96 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,98 +39,91 @@ You can defined template files with Jinja-tokens to generate source code files i
</resources>
```

## Setup in Figma
Nothing to do here really! All you need is the file key from the URL. You can then use this in the configuration (see below).
## Getting started

We use FigEx to export a "style library" file which is not containing the actual visual designs but only colors, text styles, icons and dimensions. The design files then reference this style library.
This is a small sample file: https://www.figma.com/design/0VIabis5OosbFC3Q1tYXnT
See the files in the `samples` for a few examples! You can download the `samples` folder to get
started. Go to the Figma [Variables playground example](https://www.figma.com/community/file/1234936397107899445) and select "Open in Figma".
The file will open in your workspace and the URL will look like this:

## How to use standalone on macOS / Linux
1. Make sure Java is installed on your machine, run `java --version` to confirm
2. Download a `figex.zip` from the [release list](https://github.com/iodigital-com/figex/releases)
3. Extract the zip file and place it in your system
4. Add the extracted directory to your system's `$PATH` (macOS)
5. Create a config file for your project, see the `sample/config.json`
6. Create a Figma personal access token
7. Run ` export FIGMA_TOKEN="your token"`
8. Run `figex -c "path to your config"`
```
https://www.figma.com/design/{{figmaFileKey}}/Variables-playground-(Community)?node-id=41-11&t=SPTJno70ETNtkk5D-0
```

Copy the `{{figmaFileKey}}` section and replace the file key in `samples/config.json`. Now select your profile picture in the top
left of Figma and select "Settings", then scroll down to "Personal access tokens" and create a new one. Now run figex.
This will create a `samples_output` folder next to the `samples` folder. Enjoy!

### Option A: Using the gradle plugin
FigEx can be used as part of your gradle build system. Add the plugin to the root build.gradle.kts` file:

```kotlin
plugins {
id("com.iodigital.figex") version "{latest-version}"
}

figex {
figmaToken = "{Figma Token}"
configFile = file("path/to/config.json")
}
```

Now you can run `./gradlew exportFigma` to export all your Figma resources!


### Option B: Using shell
FigEx can be run standalone from the shell.

## How to use standalone on Windows
1. Make sure Java is installed on your machine, run `java --version` to confirm
2. Download a `figex.zip` from the [release list](https://github.com/iodigital-com/figex/releases)
3. Extract the zip file and place it in your system
4. Create a config file for your project, see the `sample/config.json`
5. Create a Figma personal access token
6. Run `set FIGMA_TOKEN="your token"`
7. Run `java -jar "path to figex jar" -c "path to your config"`

## How to use as part of your Gradle build system
You can also use FigEx as a gradle dependency, e.g. for your `buildSrc` in an Android Studio project.
6. Create a Figma personal access token

#### macOS / Linux
```shell
export FIGMA_TOKEN="Figma token"
figex -c "path to your config"
```

1. Add JitPack to your `buildSrc`'s `settings.gradle`
#### Windows
```shell
set FIGMA_TOKEN="your token"
java -jar "path to figex jar" -c "path to your config"
```
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {

### Option C: Using the core library
You can make use of the FigEx core library in any Java/Kotlin project by including it in your `build.gradle` file:

```kotlin
repositories {
mavenCentral()
maven { url = uri("https://jitpack.io" }
}
}
```

2. Add FigEx to your `buildSrc`'s `build.gradle`
```
implementation("com.github.iodigital-com:figex:Tag|)
```
**Important:** FigEx makes use of `com.android.tools:sdk-common` as a build dependency, this means there can be a conflict with your Android Gradle Plugin version. If you experience sync issues after adding FigEx, try the following. FigEx will not be able to covert SVG to Android Vector graphics. AGP 8.4.0-8.5.0 is currently tested and working. Consider using FigEx as a standalone tool as an alternative (see above).
```
implementation("com.github.iodigital-com:figex:Tag") {
exclude("com.android.tools")
}
implementation("com.iodigital.figex:figex-core:{latest-version}")
```

In your code you can now use FigEx:

3. Add a Gradle task in the `buildSrc`
```
abstract class ExportFigmaTask : DefaultTask() {
@get:InputFile
var configFile: File = File(IoBuildConfig.rootDir, "config/figex/figex.json")
@get: Input
var token: String = ""
@TaskAction
fun action() = runBlocking {
try {
export(
configFile = configFile,
figmaToken = requireNotNull(
System.getenv("FIGMA_TOKEN") ?: token.takeIf { it.isNotBlank() }
) { "Missing Figma token" },
)
} catch (e: Exception) {
e.printStackTrace()
throw e
}
}
}
```kotlin
FigEx.exportBlocking(
configFile = "path/to/config.json",
figmaToken = "{Figma Token}",
)
```

4. Register the task in your main `build.gradle`
```
tasks.register<ExportFigmaTask>("updateFigmaResources").configure {
token = ...
configFile = File(...) // Optional, defaults to config/figex/figex.json
}
```

5. Update your Figma resources via gradle:
```
gradle updateFigmaResources
```
## Setup in Figma
Nothing to do here really! All you need is the file key from the URL. You can then use this in the configuration (see below).

We use FigEx to export a "style library" file which is not containing the actual visual designs but only colors, text styles, icons and dimensions. The design files then reference this style library.
This is a small sample file: https://www.figma.com/design/0VIabis5OosbFC3Q1tYXnT


## Modes in Figma
Modes in Figma allow you to have multiple values for the same variable. This can be used for light and dark modes, different brand styles or languages.
FigEx does see the modes you configured in Figma, but not the names you gave these modes. Initially, the modes will show up as their id, e.g. `834:0`.
In the logs, FigEx will list modes that you did not give a name to yet and also some sample values. Using the values, you can identify the modes and then
add a `modeAliases` entry in the `config.json` file. Afterwards, FigEx will represent the modes by their alias instead of their id.

## Config file

Expand All @@ -144,6 +137,7 @@ See the example config in the `samples` directory.
- `destinationPath`: The path to where the generated file should be written
- `defaultMode`: The default mode to be used for the values. If the `defaultMode` is e.g. `test` then `color.test.argb` is the same as `color.argb`
- `templateVariables`: A map of extra variables for the template. If you define `test` here you can later use `{{ test }}` in your template file
- `filter`: A template that should read `true` to include a value in the export
- `"type": "icons"` is used to export icons and illustrations
- `format`: One of `svg`, `pdf`, `png`, `webp` or `androidxml`
- `filter`: A template that should read `true` to include a component in the export
Expand Down Expand Up @@ -185,7 +179,9 @@ This templating is used in the `filter` and `fileNames` configurations.
This templating is used in the file at the `templatePath` configuration.

- `colors`: A list of `Color` objects
- `dimens`: A list of `Dimension` objects
- `floats`: A list of `Float` objects
- `strings`: A list of `String` objects
- `booleans`: A list of `Boolean` objects
- `text_styles`: A list of `TextStyle` objects
- `figma`: A `Figma` object
- `date`: The current date
Expand All @@ -197,16 +193,32 @@ This templating is used in the file at the `templatePath` configuration.
- `a`, `r`, `g`, `b`: The alpha, red, green and blue value 0..1 (for the default mode)
- `a255`, `r255`, `g255`, `b255`: The alpha, red, green and blue value 0..255 (for the default mode)
- `argb`: The argb hex string (for the default mode)
- `modeA`: A nested color object for `modeA`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modeB`: A nested color object for `modeB`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modeA`: A nested `Color` object for `modeA`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modeB`: A nested `Color` object for `modeB`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modes`: A list of nested `Color` objects for each mode in which the `name` field represents the name of the mode, not the color

#### Dimension
#### Float
- `name`: A name object
- `value`: The value (for the default mode)
- `modeA`: A nested color object for `modeA`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modeB`: A nested color object for `modeB`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modeA`: A nested `Float` object for `modeA`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modeB`: A nested `Float` object for `modeB`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modes`: A list of nested `Float` objects for each mode in which the `name` field represents the name of the mode, not the float
-
#### String
- `name`: A name object
- `value`: A string
- `modeA`: A nested `String` object for `modeA`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modeB`: A nested `String` object for `modeB`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modes`: A list of nested `String` objects for each mode in which the `name` field represents the name of the mode, not the string

#### Text style
#### Boolean
- `name`: A name object
- `value`: A boolean string
- `modeA`: A nested `Boolean` object for `modeA`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modeB`: A nested `Boolean` object for `modeB`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modes`: A list of nested `Boolean` objects for each mode in which the `name` field represents the name of the mode, not the boolean
-
#### TextStyle
- `name`: A name object
- `font_family`: As defined in Figma (for the default mode)
- `font_post_script_name`: As defined in Figma (for the default mode)
Expand All @@ -220,8 +232,9 @@ This templating is used in the file at the `templatePath` configuration.
- `text_align_horizontal`: As defined in Figma (for the default mode)
- `text_align_vertical`: As defined in Figma (for the default mode)
- `text_auto_resize`: As defined in Figma (for the default mode)
- `modeA`: A nested color object for `modeA`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modeB`: A nested color object for `modeB`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modeA`: A nested `TextStyle` object for `modeA`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modeB`: A nested `TextStyle` object for `modeB`, contains the same values as above. The name depends on your modes and the `modeAliases` in the config
- `modes`: A list of nested `TextStyle` objects for each mode in which the `name` field represents the name of the mode, not the style

#### Name
Hint: You can also use Jinja filters to modify the name, e.g. `{{ color.name|lowercase|replace("some", "other") }}`
Expand All @@ -231,24 +244,19 @@ Hint: You can also use Jinja filters to modify the name, e.g. `{{ color.name|low
- `kebab`: The name in kebab case
- `pascal`: The name in pascal case


#### Figma
- `file`: The Figma file name
- `last_modified`: Last modified date of the Figma file
- `version`: The version of the Figma file

#### Scale

- `scale`: The scale as floating point number as configured
- `name_prefix`: The prefix of the filename as configured
- `name_suffix`: The suffix of the filename as configured

## Build the project

- Clone the Git
- To test, open in Android Studio and
- Create a `.figmatoken` file containing your token in `figma-exporter/.figmatoken`
- Create a `sampleconfig.json` file with a config in `figma-exporter/.sampleconfig.json`
- Create a `.figmatoken` file containing your token in `figex-cli/.figmatoken`
- Run the `Run sample` configuration
- `./gradlew clean build` will build the project and create files
in `figma-exported/build/distributions`
- `./gradlew clean build` will build the project and create files in `figma-exported/build/distributions`
17 changes: 15 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
plugins {
//trick: for the same plugin versions in all sub-modules
alias(libs.plugins.kotlinMultiplatform).apply(false)
alias(libs.plugins.kotlinSerialization).apply(false)
alias(libs.plugins.kotlinMultiplatform) apply false
alias(libs.plugins.kotlinSerialization) apply false
alias(libs.plugins.jetbrainsKotlinJvm) apply false
alias(libs.plugins.mavenPublishing) apply false
}


val buildNumber = (findProperty("AzureBuildNumber") ?: "debug").toString().replace(".", "-")
val buildVersion = "${project.property("VERSION_NAME")}.$buildNumber"

println("##vso[build.updatebuildnumber]name=$buildVersion,code=$buildVersion,buildId=$buildNumber")
File(System.getenv("GITHUB_OUTPUT") ?: "/dev/null").appendText("build_version=$buildVersion")

subprojects {
version = buildVersion
}
48 changes: 48 additions & 0 deletions figex-cli/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinSerialization)
application
}

kotlin {
jvm()

sourceSets {
commonMain.dependencies {
implementation(libs.kotlinx.cli)
implementation(libs.ktor.core)
implementation(project(":figex-core"))
}
}
}

distributions {
main {
distributionBaseName.set("figex")
contents {
into("") {
val jvmJar by tasks.getting
from(jvmJar)
from("src/figex")
}
into("lib/") {
val main by kotlin.jvm().compilations.getting
from(main.runtimeDependencyFiles)
}
exclude("**/figma-exporter")
exclude("**/figma-exporter.bat")
}
}
}

tasks.withType<Jar> {
doFirst {
manifest {
val main by kotlin.jvm().compilations.getting
attributes(
"Main-Class" to "com.iodigital.figex.MainKt",
"Class-Path" to main.runtimeDependencyFiles.files.joinToString(" ") { "lib/" + it.name }
)
}
}
}
Loading

0 comments on commit 644905c

Please sign in to comment.