- Technical preconditions
- Build Design System Toolbox
- Documentation
- Run tests
- Build phases
- Targets
- Certificates, profiles and identifiers
- Update dependencies with Renovate
- Developer Certificate of Origin
- Commits, changelog, release note, versioning
- Use of Gitleaks
- Linter
- CI/CD
You should check wether or not you have the tools in use in the project like Fastlane, SwiftLint, SwiftFormat, etc. You can have a look for example in the THIRD_PARTY.md file which lists any dependencies and tools we use at different levels (SDK, design system toolbox app, projects). Have a look on the locks file to know which versions we are using (Podfile, Podfile.lock, Packages.swift, Package.resolved, Gemfile, Gemfile.lock).
If some tools are missing, pick the suitable command line below and check versions:
# Use Bundler to install a major part of dependencies (thanks to Gemfile and Gemfile.lock files)
bundle install
# Use CocoaPods to install other dependencies not available as rubygems (thanks to Podfile and Podfile.lock files)
bundle exec pod install --repo-update
# Update your references
brew update
# For Periphery (https://github.com/peripheryapp/periphery) for dead code hunt (at least 2.21.0)
brew install peripheryapp/periphery/periphery
# For gitleaks (https://github.com/gitleaks/gitleaks) for secrets leaks hunt (at least 8.18.1)
brew install gitleaks
# or `brew reinstall gitleaks` to get updates if old version installed
# For SwiftLint (at least 0.57.0)
brew install swiftlint
# or `brew reinstall swiftlint` to get updates if old version installed
# For SwiftFormat (at least 0.52.4)
brew install swiftformat
# or `brew reinstall swiftformat` to get updates if old version installed
# For xcodes (at least 1.5.0)
brew install xcodesorg/made/xcodes
# or `brew reinstall xcodesorg/made/xcodes` to get updates if old version installed
Ensure you have the suitable Ruby version. We recommend the use of rbenv to load the suitable version of ruby. We use here Ruby 3 (>= 3.3). If you are not used to this tool:
# List available local versions of Ruby
rbenv install --list
# Apply the expected x.y.z version of Ruby (if listed previously with the command above)
rbenv global 3.3.5
# If you don't have the expected x.y.z version of Ruby, run:
# > brew update && brew upgrade ruby-build
# > rbenv install x.y.z
# then
# > rbenv global x.y.z
# Check Ruby version
ruby --version
We use also for our GitLab CI runners Xcode 16, we suggest you use this version or newer if you want.
Xcode 16 and Swift 6 are used for this project. You must use this configuration.
To build the demo application follow those steps:
cd DesignToolbox
bundle exec pod install
- Open DesignToolbox.xcworkspace
- Select DesignToolbox scheme
- Build and run the Application on your device ou simulator
The documentation is based on the Swift documentation with DocC. We use Xcode to build the documentation then export each DocC catalog as DocC archive and, finaly, merge the HTML documentations for the online version.
The documentation can be built from Xcode with Product > Build Documentation.
The uploadWebDoc.sh
script helps to build the HTML version of documentation and compress it in ZIP file, and also can update
the online version based on GitHub Pages, this version is hosted in the gh-pages GitHub branch.
To run these unit tests follow some steps:
cd DesignToolbox
bundle exec pod install
- Open DesignToolbox.xcworkspace
- Select DesignToolbox scheme
- Run tests (Product -> Test)
Unit tests care have been implemented for several reasons.
First, we don't have too much control on the raw tokens values. We rely on the Figma design tool which outputs the tokens in a JSON file. And this file will be parsed to as to generate Swift files. But if there are inconsistencies in the Figma side or in the parser side, the inconsistencies will be spread in our code base. It is not useful to define unit tests for raw tokens to test their values ; in fact they exist here to be updated. But we wan still check other things like the relationship between them. For example a grid100 should always be less or equal than a grid100. Some color100 should be always lighter than a color200, etc, etc. A small typo should be always smaller or with the sale size has a one-step-bigger typo.
Then, we want to know when tokens have been removed so as to warn our users and keep release notes and changelog clean. If we don't spot such changes, maybe some users will be impacted.
Finally, we ensure our themes can override any semantic tokens. Themes are in fact a set of values for the whole universe of semantic tokens, and if a theme cannot override a semantic token, there could be an issue. Unit tests also help us to find if some tokens have been removed before releasing the library.
To run these UI tests follow some steps:
cd DesignToolbox
bundle exec pod install
- Open DesignToolbox.xcworkspace
- Select DesignToolboxSnapshotsTests scheme
- Select iPhone 16 Pro simulator (the device used to tests and views rendering)
- Run tests (Product -> Test)
Beware, if you add new UI tests using swift-snapshot-testing library, you may have new tests which fail at first time. Indeed for new tests the tool makes snapshots of the views, thus for the first run no previews exist making the tests fail. You should run the tests twice for new tests.
Such tests here are used to as to be sure the look and feel of any components and tokens rendering remain the expected ones.
Any interface modifications require regenerating the illustrations using the tool, i.e. run the tests twice. The reference illustrations have already been saved within the project.
Note today because the demo app (Design System Toolbox) is hosted in the repository, the tests assets are versioned too, thus the Swift Package will be heavy when being downloaded because Xcode downloads the entire repository. When the demo app app will be extracted to an internal repository, the Swift Package will be lighter.
The device under tests is a simulator of iPhone 16 Pro, in portrait mode, with no a11y feature enabled, and a text size of 100% in english mode.
- Locate where are the reference images:
- In the Package directory, you will find the reference screenshots for the Orange and Inverse themes (Light/Dark), which will serve as comparison baselines.
OUDS -> DesignToolbox -> DesignToolboxSnapshotsTests -> __Snapshots__
- Navigate to the project :
- Open your project in Xcode and go to a directory containing tests (e.g. here OUDSTokensOpacityUITests):
DesignToolbox -> DesignToolboxSnapshotsTests -> OUDSTokensOpacityUITests -> OUDSTokensOpacityUITests.swift
- Open a test file (e.g. here OUDSTokensOpacityUITests):
- Open the file
OUDSTokensOpacityUITests.swift
.
- Open the file
- Run the snapshot test (success):
The snapshot tool fetched the reference image to compare it against the current screen and detected no differences, resulting in a success
-
Run the snapshot test (failure):
-
We will deliberately change the image by setting the
OpacityOpaque
token toOpacityInvisible
in classOpacityTokenPage.swift
-
Test result failure :
The swift-snapshot-testing tool indicates that the issue originates from the transparent token illustration. We can observe that there are two paths: the first corresponds to our reference illustration (the one we intend to base our comparison on), while the second path is the illustration used for the current image of the application. You can open both paths and visually compare the differences.
-
-
Verify the output:
- It is recommended to use the
Show the Report Navigator
tool in Xcode :
- In Xcode go to :
DesignToolboxSnapshotsTests -> DesignToolboxSnapshotsTests/DesignToolboxSnapshotsTests
- It is recommended to use the
-
Comparison (reference and failure):
Reference:
Failure:
The project contains several custom build phases so as to automatize several steps:
- SwiftLint will run the linter on the sources
Note that sources headers are defined in IDETemplateMacros.plist inside DesignToolbox/DesignToolbox.xcworkspace/xcshareddata
The Xcode project contains two targets:
- DesignToolbox for the demo application
- Periphery to look for dead code in the source code
- DesignToolboxSnapshotsTests for UI tests in demo app
We choose to use Xcode automatic signing for debug builds of the app so as to make easier onboarding of newcomers in development team, and also to prevent to update provisioning profiles with individual developers certificates each team someone wants to build the app and also to prevent to register each new build device. You may need to be part of our team if you want to build in debug mode. Note the bundle identifier here for local builds is com.orange.ouds.demoapp-debug, with a -debug suffix so as to prevent any local build to be replaced by TestFlight builds which have com.orange.ouds.demoapp identifiers.
However for release builds we use a dedicated provisioning profile built with of course a distribution certificate(.p12 format with private key, not .cer) and the bundle identifier com.orange.ouds.demoapp
for our Apple Team France Telecom (MG2LSJNJB6)
. Thus you won't be able to build and sign in release mode without this provisioning profile and this distribution certificate. These elements are stored in our local GitLab CI runners and must not be available outside.
Sometimes dependencies should be updated, with for example warnings of Renovate bot.
Here is the list of files to update to keep the project clean:
- CHANGELOG (to note for releases the update of the version)
- THIRD_PARTY (because we list all third-party components, it is a good practice)
- Of course, update and save in your VCS the new states of the Podfile, Package.swift or Gemfile for example (and do not forget locks!)
Maybe you will need to update your pods repo before if you updated a Pod:
bundle exec pod install --repo-update
A GitHub Action bot has been plugged in the repository so as to check wether or not the DCO is applied for commits.
Try as best as possible to apply conventional commits rules. Keep in mind to have your commits well prefixed, and with the issue number between parenthesis at the end, and also if needed the pull request issue number. If your commits embed contributions for other people, do not forget to add them as co-authors. All of you should also comply to DCO.
Your commit message should be prefixed by keywords you can find in the specification:
fix:
feat:
build:
chore:
ci:
docs:
style:
refactor:
perf:
test:
You can add also ! after the keyword to say a breaking change occurs, and also add a scope between parenthesis like:
feat!:
breaking change because..feat(API)!:
breaking change in the API because..feat:
add something in the API...
We can add metafields picked from this good guideline in the commit messages. This is not mandatory (yet) but a good practice and quite interesting to know who reviewed and validated what. You must mention co-authors (Co-authored-by). You should add who are code reviewers (Reviewed-by), evolutions testers (Tested-by) and if needed ackers (Acked-by).
For example, for issue n°123 and its pull request n°456, tested by Anton, Iman, Maxime, Pierre-Yves and Benoit, reviewed by Ludovic, authored by Tayeb and Pierre-Yves, and acked by Stephen:
refactor: update some things colors and design of the demo app (#123) (#4562)
Some things have been refactored to make incredible things.
Tested-by: Iman Assabah <iman.assabah.ext@orange.com>
Tested-by: Anton Astafev <anton.astafev@orange.com>
Tested-by: Benoit Suzanne <benoit.suzanne@orange.com>
Tested-by: Maxime Tonnerre <maxime.tonnerre@orange.com>
Tested-by: Pierre-Yves Ayoul <pierre-yves.ayoul@orange.com>
Reviewed-by: Ludovic Pinel <ludovic.pinel@orange.com>
Acked-by: Stephen McCarthy <stephen.mccarthy@orange.com>
Co-authored-by: Tayeb Sedraia <tayeb.sedraia@orange.com>
Co-authored-by: Pierre-Yves Lapersonne <pierreyves.lapersonne@orange.com>
Signed-off-by: Tayeb Sedraia <tayeb.sedraia@orange.com>
Signed-off-by: Pierre-Yves Lapersonne <pierreyves.lapersonne@orange.com>
You should refer to the dedicated page in the wiki for more details.
Keep in mind the commit adding tokenator updates in the codebase must be formatted like
chore(🤖): update `OpacityRawTokens` (tokenator generation 20241021134644) (#225)
i.e. precise the tokens updated, the tokenator generation timestamp and the pull request number.
If you know what is the token library version, add it in the commit body, like:
chore(🤖): update `OpacityRawTokens` (tokenator generation 20241021134644) (#225)
Tokens library v0.4.1
We try also to apply keep a changelog, and semantic versioning both with conventional commits.
We can generate a RELEASE_NOTE.md
file using the Git history and git cliff tool.
Today we update the unique CHANGELOG manualy, but you can find in the wiki more details about the use of git-cliff
To generate a release note:
# Install git-cliff
brew install git-cliff
# Run the command
# where X is the starting tag and Y the ending tag
git-cliff --config .github/cliff.toml --output RELEASE_NOTE.md X..Y
Gitleaks can be used to check if secrets can be leaked or not. A GitHub Action has been integrated to the repository with a configuration file defined in /github/workflows named gitleaks-action.yaml. It will launch the Gitleaks tool automatically.
However this tool does not detect plain API key mixed in URL, that is a reason why Gitleaks can be called in a pre-commit hook, using the giteaks.toml at the root of the project.
To call Gitleaks in pre-commit hooks, create a file named pre-commit inside .git/hooks (then run chmod u+x
in the file).
Then place the bash code below in this file:
# Run Gitleaks before commits
echo "Running pre-commit hook: Use of gitleaks"
gitleaks detect -v -l debug --source .
# If the command fails, prevent the commit
if [ $? -ne 0 ]; then
echo "Pre-commit hook failed. Aborting commit."
exit 1
fi
Or just run when you want the command:
gitleaks detect -v -l debug --source .
Note that we face some issues about the use of Gitleaks GitHub Action and Gitleaks as CLI command, for fur further details see #131, #132 and #1331.
Remember Gitleaks is also used in GitHub project side thanks to the dedicated GitHub Action but these controls are done online once commits have left the local environment.
We use SwiftLint in this project so as to be sure the source code follows defined guidelines for the syntax and other points. You must run SwiftLint in CLI or using Xcode to be sure you don't keep and submit warnings. In most of cases you must fix warnings, or explain why in your commits and pull request comments you choose to disable them.
Today, only in very few cases some SwiftLint warnings are disabled at files (or lower) level:
- in tests classes
- in files containing tokens which will be generated
- in tokens providers
The warnings which can be disabled for token files:
- missing_docs: because tokens will be generated without documentation by the tokenator
- identifier_name: because the name of the tokens are defined in Figma and strongly related to the design system, and in they can be long
- line_length: because tokens definition can take a lot of place
- file_length: because the files containing declarations or definitions of tokens can be very long
The warnings which can be disabled for test classes files and mocks files:
- identifier_name: because of length of tokens names
- type_name: because stringly related to the types under test, which can have a long name
- line_length: because of length of tokens names
- file_length: because of the amount of tokens to test
- type_body_length: because we can have a lot of tests to do
- function_body_length: because we can have function with a lot of assertions
- force_try: because we can need tod efine some configuration variable we are sure they work (like regxp)
- required_deinit: because we do not need to manage init and deinit of test classe
- implicitly_unwrapped_optional: because for declaration of themes to test we bang!
Do not forget if possible to enable the warnings in the end of the file to reduce as much as possible the scope of the disabled warnings. Disable warnings only if needed.
We use GitHub Actions so as to define a workflow with some actions to build demo application and test the library. It will help us to ensure code on pull requests or being merged compiles and has all tests green. This workflow is defined in this YAML, and makes build, unit tests and UI tests.
We have also a gitleaks workflow making some scans on the code to look fo secrets leaks, defined in this YAML.
A dedicated workflow has been defined so as to run checks on localizables to find is some wording is missing (thanks to SwiftPolyglot).
We use also two GitHub apps making controls on pull requests and defining wether or not prerequisites are filled or not. There is one control to check if PR template are all defined , and one if DCO is applied.
Finaly we have this GitHub Action using SwiftLint to ensure no warnings are in our codebase.
We use GitLab CIfor CI/CD with our own runners so as to keep private our sensitive files likes certificates and provisioning profiles. Our current plan does not allow to make GitHub mirroring, so we use GitHub HTTP REST API to download sources, before using Xcode to build and sign. However of course you will have to define all the variables, secrets and have the mandatory files.
You can find more details about the pipelines, how to set up runners and scripts to use in the wiki.
In few words, there is a pipeline containing some stages and jobs to build alpha, nightly/beta and production releases.