Travis CI Android build keep failing with gradlew no such file - java

my travis ci build keeps failing with,
$ chmod +x /.gradlew
chmod: cannot access ‘/.gradlew’: No such file or directory
The command "chmod +x /.gradlew" failed and exited with 1 during .
I tried all the suggestions, different yml files but cant get rid of this error.
My travis yml is on root directory, the here is my folder structure
root: /src .gitignore .travis.yml
src: /client /server
client: /app /gradle/wrapper build.gradle gradle.properties gradlew gradlew.bat settings.gradle
Here is my travis.yml
sudo: false
language: android
jdk:
- oraclejdk8
android:
components:
- tools
- platform-tools
- tools
# The BuildTools version used by your project
- build-tools-25.0.3
# The SDK version used to compile your project
- android-25
- extra-google-google_play_services
- extra-google-m2repository
- extra-android-m2repository
- addon-google_apis-google-19
before_install:
- chmod +x /.gradlew
script:
- "/.gradlew clean build"
notifications:
email: false
and here is my build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
maven { url "https://maven.google.com" }
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.3.3'
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Any suggestions? Thanks

To debug this you could change the before_install section to print current directory and list its contents.
before_install:
- pwd
- ls -la
- chmod +x /.gradlew

Add this .yml file
- name: Check Directory
run: - pwd
- ls -la
- cd android && chmod +x ./gradlew
- name: Build Command
run: cd android && chmod +x ./gradlew

Related

Jenkins permission denied to /usr/share/maven cannot build tests

I am running jenkins on AWS.
i am running a pipline through a jenkinsfile
node{
stage('Get Git'){
checkout scm
}
stage('Build'){
def mvnHome = tool name: 'mvn', type: 'maven'
sh "${mvnHome} install -DskipTests"
}
stage('Deploy'){
echo "Deploy code here"
}
}
when i run this i get the following errors
(i have left out the git repo stage down below which i working fine)
node{
stage('Get Git'){
checkout scm
}
stage('Build'){
def mvnHome = tool name: 'mvn', type: 'maven'
sh "${mvnHome}/bin/mvn install -DskipTests"
}
stage('Deploy'){
echo "Deploy code here"
}
}
fixed the issue with adding ${mvnHome}/bin/mvn.

Alpine Docker Image to run JLink executable

My current Docker image is approximately 200MB. My goal is to reduce that as much as possible, so I thought the alpine image would be a great way to achieve that. However, I keep encountering roadblocks.
It's a multi-module libGDX project that uses Java 14 and its preview features, along with JLink (from JPackage) to produce a custom (lightweight) JRE.
I only care to deploy the server module, which depends on the common module (which the client module also uses).
I'll be providing all the necessary files below, but in case I'm missing something, here is a link to the repo: https://github.com/payne911/marvelous-bob/tree/dev
Current problem
I either get:
when executing ./server: file not found for an executable file which I know exists and is executable where I'm trying to run it in the Docker Image
when executing apk add --no-cache someLibrary: a segfault: core dumped when trying to download libraries in my ENTRYPOINT which I thought were required to execute that file (libstdc++ and libc6-compat)
Goal
Reusable lightweight Docker Image which contains everything to run libGDX executables generated by JLink.
Current files which are in a working state and I'm trying to refactor
Dockerfile
FROM openjdk:14-jdk-alpine
COPY server/build/libs/server-all.jar /app.jar
COPY utils/deploys/bootstrap.sh /bootstrap.sh
RUN chmod +x /bootstrap.sh
EXPOSE 80
ENTRYPOINT ["./bootstrap.sh"]
bootstrap.sh
#!/usr/bin/env sh
echo "========== LAUNCHING SERVER APP ========="
apk add --no-cache libstdc++
java --enable-preview -jar /app.jar
build.gradle files
root
buildscript {
repositories {
mavenLocal()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
jcenter()
google()
}
dependencies {
}
}
// to force download of sources and JavaDoc
plugins {
id 'java'
id 'idea'
}
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}
allprojects {
version = '1.0'
ext {
appName = "marvelous-bob"
gdxVersion = '1.9.10'
roboVMVersion = '2.3.7'
box2DLightsVersion = '1.5'
ashleyVersion = '1.7.0'
aiVersion = '1.8.2'
slf4j = '1.7.26'
logback = '1.2.3'
lombok = '1.18.12'
kryonet = '2.22.6'
reflectionsVersion = '0.9.12'
pieMenuVersion = '4.2.0'
shapeDrawer = '2.3.0'
}
repositories {
mavenLocal()
mavenCentral()
jcenter()
google()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven { url "https://oss.sonatype.org/content/repositories/releases/" }
maven { url 'https://jitpack.io' }
}
}
project(":desktop") {
apply plugin: "java-library"
dependencies {
api project(":client")
api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
api "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
api "com.badlogicgames.gdx:gdx-tools:$gdxVersion"
}
}
project(":client") {
apply plugin: "java-library"
dependencies {
api project(":common")
api "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
api "space.earlygrey:shapedrawer:$shapeDrawer"
api "com.github.payne911:PieMenu:$pieMenuVersion"
}
}
project(":server") {
apply plugin: "java-library"
dependencies {
api project(":common")
api "com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion"
api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
}
}
project(":common") {
apply plugin: "java-library"
dependencies {
api "com.badlogicgames.gdx:gdx:$gdxVersion"
api "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
api "com.badlogicgames.box2dlights:box2dlights:$box2DLightsVersion"
api "com.badlogicgames.gdx:gdx-ai:$aiVersion"
// todo: do we need both logging libraries? p.s. kryonet uses Minlog
api "org.slf4j:slf4j-api:$slf4j"
api "ch.qos.logback:logback-classic:$logback"
api "com.github.crykn:kryonet:$kryonet"
api "org.reflections:reflections:$reflectionsVersion"
}
}
common
plugins {
id 'java'
id 'io.freefair.lombok' version '5.1.0'
}
sourceCompatibility = 14
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
tasks.withType(JavaCompile) {
options.compilerArgs += "--enable-preview"
}
tasks.withType(Test) {
jvmArgs += "--enable-preview"
}
tasks.withType(JavaExec) {
jvmArgs += '--enable-preview'
}
sourceSets.main.java.srcDirs = [ "src/" ]
jar {
from('src') {
include '**/*.properties'
}
}
server
plugins {
id 'java'
id 'io.freefair.lombok' version '5.1.0'
id 'com.github.johnrengelman.shadow' version '5.2.0'
}
sourceCompatibility = 14
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
tasks.withType(JavaCompile) {
options.compilerArgs += "--enable-preview"
}
tasks.withType(Test) {
jvmArgs += "--enable-preview"
}
tasks.withType(JavaExec) {
jvmArgs += '--enable-preview'
}
sourceSets.main.java.srcDirs = [ "src/" ]
jar {
project.version="" // to remove version number from the built jar's name
manifest {
attributes 'Main-Class': 'com.marvelousbob.server.BobServerLauncher'
}
}
GitHub Actions yml file
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout#v2
- name: Setup with Java 14
id: java-setup
uses: actions/setup-java#v1
with:
java-version: 14
- name: Allow GitHub Actions to run Gradlew
run: chmod u+x gradlew
- name: Run Gradle build
id: gradle-build
uses: eskatos/gradle-command-action#v1
with:
arguments: shadowJar
# more stuff ...
What I've tried
So many things, for days! Here was the state of my last attempt.
Dockerfile
FROM alpine:3.7
COPY server/build/jpackage/server /server
FROM openjdk:14-jdk-alpine
COPY server/build/libs/server-all.jar /app.jar
COPY utils/deploys/bootstrap.sh /bootstrap.sh
RUN chmod +x /bootstrap.sh
EXPOSE 80
ENTRYPOINT ["./bootstrap.sh"]
bootstrap.sh
#!/bin/sh
echo "========== LAUNCHING SERVER APP ========="
cd /server/bin
chmod +x server
./server
build.gradle
root
Same as other one.
common
Same as other one.
server
plugins {
id 'java'
id 'io.freefair.lombok' version '5.1.0'
id 'org.beryx.runtime' version '1.8.4'
}
sourceCompatibility = 14
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
tasks.withType(JavaCompile) {
options.compilerArgs += "--enable-preview"
}
tasks.withType(Test) {
jvmArgs += "--enable-preview"
}
tasks.withType(JavaExec) {
jvmArgs += '--enable-preview'
}
sourceSets.main.java.srcDirs = [ "src/" ]
mainClassName = "com.marvelousbob.server.BobServerLauncher"
task dist(type: Jar) {
project.version="1" // adjust the CI/CD if you change this
manifest {
attributes 'Main-Class': project.mainClassName
}
from {
configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
with jar
destinationDirectory = file("$buildDir/lib")
}
jpackageImage.dependsOn dist
dist.dependsOn classes
// JLink configuration to minimize size of generated jar
runtime {
options = ['--strip-debug',
'--compress', '2',
'--no-header-files',
'--no-man-pages',
'--strip-native-commands',
'--vm', 'server']
modules = ['java.base' ,
'java.desktop',
'jdk.unsupported']
distDir = file(buildDir)
jpackage {
//jpackageHome = '/usr/lib/jvm/open-jdk'
mainJar = dist.archiveFileName.get()
}
}
Github Actions yml file
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout#v2
- name: Setup with Java 14
id: java-setup
uses: actions/setup-java#v1
with:
java-version: 14
- name: Building with Gradlew
run: |
chmod u+x gradlew
./gradlew server:jpackageImage
# more stuff ...
#deduper seems to have figured it out (see https://chat.stackoverflow.com/rooms/222569/discussion-between-deduper-and-payne), but I didn't have time to go back and make sure his analysis is right.
In theory, it does make a lot of sense, so I'm posting it nonetheless as an answer:
I'm pretty sure the main cause of the errors you describe in your question is because you built the runtime on either Windows 10 or Ubuntu; then tried to deploy and run the runtime on Alpine.
I won't accept my answer because I haven't made a confirmation of this yet, but I'm nonetheless posting it in case others encounter this problem and want a probable clue.

Docker gradle access denied

i tried to build docker image using docker-compose but i got this error
/bin/sh: 1: ./gradle: Permission denied
my Dockerfile is
FROM gradle:6.5.0-jdk11 AS TEMP_BUILD_IMAGE
ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME
COPY build.gradle.kts settings.gradle.kts $APP_HOME
COPY gradle $APP_HOME/gradle
COPY --chown=gradle:gradle . /home/gradle/src
USER root
RUN chown -R gradle /home/gradle/src
RUN ./gradle build || return 0
COPY . .
RUN ./gradle clean build
FROM openjdk:11-jdk
ENV ARTIFACT_NAME=app.jar
ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME
COPY --from=TEMP_BUILD_IMAGE $APP_HOME/build/libs/$ARTIFACT_NAME .
ENTRYPOINT exec java -jar ${ARTIFACT_NAME}
and this is what i got in the shell
D:\Docker>docker-compose up -d
Building config-server
Step 1/17 : FROM gradle:6.5.0-jdk11 AS TEMP_BUILD_IMAGE
---> a001e5b2850a
Step 2/17 : ENV APP_HOME=/usr/app/
---> Using cache
---> 07cf3a267c37
Step 3/17 : WORKDIR $APP_HOME
---> Using cache
---> 87be3841245e
Step 4/17 : COPY build.gradle.kts settings.gradle.kts $APP_HOME
---> Using cache
---> eff11fa2348e
Step 5/17 : COPY gradle $APP_HOME/gradle
---> Using cache
---> 10fca093f82e
Step 6/17 : COPY --chown=gradle:gradle . /home/gradle/src
---> b1f888f97818
Step 7/17 : USER root
---> Running in bde4f2d435fe
Removing intermediate container bde4f2d435fe
---> e8fba435db0c
Step 8/17 : RUN chown -R gradle /home/gradle/src
---> Running in d88ea2196f38
Removing intermediate container d88ea2196f38
---> b5b4727dd51f
Step 9/17 : RUN ./gradle build || return 0
---> Running in d218205301d9
/bin/sh: 1: ./gradle: Permission denied
Removing intermediate container d218205301d9
---> da37b296879b
Step 10/17 : COPY . .
---> e6cfac4a75a3
Step 11/17 : RUN ./gradle clean build
---> Running in 34480bf73106
/bin/sh: 1: ./gradle: Permission denied
ERROR: Service 'config-server' failed to build: The command '/bin/sh -c ./gradle clean build' returned a non-zero code: 126
how can i solve this error please?
You are using a base image which has gradle by itself. So, you don't need to copy gradle explicitly. Just remove ./ from gradle RUN.
RUN gradle build || return 0
COPY . .
RUN gradle clean build
Output
Step 9/17 : RUN gradle build || return 0
---> Running in fd470c45e443
Welcome to Gradle 6.5!
Here are the highlights of this release:
- Experimental file-system watching
- Improved version ordering
- New samples
For more details see https://docs.gradle.org/6.5/release-notes.html
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :buildEnvironment
------------------------------------------------------------
Root project
------------------------------------------------------------
classpath
No dependencies
A web-based, searchable dependency report is available by adding the --scan option.
BUILD SUCCESSFUL in 25s
1 actionable task: 1 executed
Removing intermediate container fd470c45e443
---> dd4056a53129
Step 10/17 : COPY . .
---> 3a46c7e9d9bb
Step 11/17 : RUN gradle clean build
---> Running in c3341e91aef0
Welcome to Gradle 6.5!

Can't build gradle application inside docker

I am trying to build an application with gradle from within a docker container.
This is my Dockerfile:
FROM openjdk:8-jdk
#install git
RUN apt-get install -y git
RUN git clone https://github.com/SFRJ/yurl.git
#install gradle
RUN wget https://downloads.gradle-dn.com/distributions/gradle-6.5-bin.zip
RUN unzip gradle-6.5-bin.zip
ENV GRADLE_HOME /gradle-6.5
ENV PATH $PATH:/gradle-6.5/bin
#compile and run app
RUN cd yurl
RUN gradle clean build --rerun-tasks --no-build-cache
ENTRYPOINT ["java", "-jar", "/yurlapp.jar"]
Everything goes well until the point where the build command is executed. It throws the following:
Step 9/10 : RUN gradle clean build --rerun-tasks --no-build-cache
---> Running in a25d344c3571
Welcome to Gradle 6.5!
Here are the highlights of this release:
- Experimental file-system watching
- Improved version ordering
- New samples
For more details see https://docs.gradle.org/6.5/release-notes.html
Starting a Gradle Daemon (subsequent builds will be faster)
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring root project ''.
> The project name must not be empty. Set the 'rootProject.name' or adjust the 'include' statement (see https://docs.gradle.org/6.5/dsl/org.gradle.api.initialization.Settings.html#org.gradle.api.initialization.Settings:include(java.lang.String[]) for more details).
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 3s
ERROR: Service 'yurlapp' failed to build: The command '/bin/sh -c gradle clean build --rerun-tasks --no-build-cache' returned a non-zero code: 1
I just don't understand what it is. The app builds perfectly fine when I run the same command outside of docker but inside docker fails. This is not a multi module project not sure why it complains about rootProject.
I can confirm that I have a settings.gradle file and inside I have this
rootProject.name = 'yurl'
Also this is my build.gradle
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath "org.postgresql:postgresql:42.2.11"
}
}
plugins {
id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
id 'org.flywaydb.flyway' version '6.3.0'
id 'nu.studer.jooq' version '4.1'
}
group 'com.javing.yurl'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
annotationProcessor 'org.projectlombok:lombok:1.18.8'
implementation 'org.jooq:jooq'
implementation 'org.jooq:jooq-codegen'
jooqRuntime 'org.postgresql:postgresql:42.2.11'
compile 'org.postgresql:postgresql:42.2.11'
implementation 'org.projectlombok:lombok:1.18.8'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-jooq'
implementation 'io.vavr:vavr:0.10.2'
implementation 'com.konghq:unirest-java:3.7.00'
testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile group: 'org.mockito', name: 'mockito-core', version: '2.1.0'
testCompile group: 'org.assertj', name: 'assertj-core', version: '3.15.0'
}
jooq {
sample(sourceSets.main) {
jdbc {
driver = 'org.postgresql.Driver'
url = 'jdbc:postgresql://yurldb:5432/yurldb'
user = 'postgres'
password = 'somepassword'
}
generator {
database() {
name = 'org.jooq.meta.postgres.PostgresDatabase'
inputSchema = 'public'
includes = '.*'
}
target {
packageName = 'com.javing.yurl'
directory = 'build/generated/java'
}
}
}
}
tasks.generateSampleJooqSchemaSource.with {
def out = new ByteArrayOutputStream()
javaExecSpec = { JavaExecSpec s ->
s.standardOutput = out
s.errorOutput = out
s.ignoreExitValue = true
s.jvmArgs '-Xmx512M'
}
execResultHandler = { ExecResult r ->
if (r.exitValue != 0) {
throw new RuntimeException('jOOQ source code generation failed:\n\n' + out.toString())
}
}
}
flyway {
url = 'jdbc:postgresql://localhost:5432/yurldb'
user = 'postgres'
password = 'somepassword'
schemas = ['public']
locations = ["filesystem:$project.projectDir/src/main/resources/db/migration"]
}
I don't know how to fix this I tried multiple things but nothing worked. I need to build this app from within docker using gradle. I know gradle is installed correctly because if I add the version command (RUN gradle -v) in the Dockerfile, I can see this printed when docker is running:
------------------------------------------------------------
Gradle 6.5
------------------------------------------------------------
Build time: 2020-06-02 20:46:21 UTC
Revision: a27f41e4ae5e8a41ab9b19f8dd6d86d7b384dad4
Kotlin: 1.3.72
Groovy: 2.5.11
Ant: Apache Ant(TM) version 1.10.7 compiled on September 1 2019
JVM: 1.8.0_252 (Oracle Corporation 25.252-b09)
OS: Linux 4.18.0-25-generic amd64
So my gradle and configuration of gradle seems ok. Also I know the installation of git and the clonning of the project is fine because if I add a RUN ls -s in my Dockerfile it will correctly print all the content of the project.
Something is wrong maybe in the build.gradle or settings.gradle file. Any idea what could be?
Can you try the below Dockerfile as have changed a little bit.
FROM openjdk:8-jdk
#install git
RUN apt-get install -y git
RUN git clone https://github.com/SFRJ/yurl.git
#install gradle
RUN wget https://downloads.gradle-dn.com/distributions/gradle-6.5-bin.zip
RUN unzip gradle-6.5-bin.zip
ENV GRADLE_HOME /gradle-6.5
ENV PATH $PATH:/gradle-6.5/bin
#compile and run app
WORKDIR yurl
RUN gradle clean build --rerun-tasks --no-build-cache
ENTRYPOINT ["java", "-jar", "/yurlapp.jar"]
The problem was, you mentioned a stageRUN cd yurl in which you are changing a directory, which is only valid for that particular stage not for the remaining stages. If you want to use that particular directory for the other stages as well. Use WORKDIR to run that operation which I did above.
P.S.:- If you want to use that directory only for 1 stage, then instead of WORKDIR use RUN cd DIR && ls -lart like command in 1 stage itself.

Action failed: gradle dependencies CircleCi

I have integrated CircleCI to run Espresso test on my app. I have taken the following circle.yml file from another online github repo and changed the android build tools and android version to 25. However when I run the build on the circleCI server, I get the following error. I have granted the execution permission in yml file.
My app Repo structure is
Action failed: gradle dependencies
export TERM="dumb"
if [ -e ./gradlew ]; then ./gradlew dependencies;else gradle dependencies;fi
bash: line 2: ./gradlew: Permission denied
export TERM="dumb"
if [ -e ./gradlew ]; then ./gradlew dependencies;else gradle dependencies;fi
returned exit code 126
Action failed: gradle dependencies
circle.yml:
general:
artifacts:
- /home/ubuntu/MyRideApp/app/build/outputs/apk/
machine:
environment:
ANDROID_HOME: /usr/local/android-sdk-linux
ADB_INSTALL_TIMEOUT: 240
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx3072M -XX:+HeapDumpOnOutOfMemoryError"'
dependencies:
pre:
- chmod +x gradlew
- touch app/google-services.json
- echo y | android update sdk --no-ui --all --filter "tools,android-25,build-tools-25.0.2,platform-tools,extra-android-m2repository,extra-android-support,extra-google-m2repository,extra-google-google_play_services"
cache_directories:
- /usr/local/android-sdk-linux/tools
- /usr/local/android-sdk-linux/build-tools/25.0.2
override:
- ANDROID_HOME=/usr/local/android-sdk-linux ./gradlew dependencies
# Comment the test stuff out (or remove it) if you don't need it.
test:
pre:
- emulator -avd circleci-android23 -no-audio -no-window:
background: true
parallel: true
- circle-android wait-for-boot
# unlock emulator
- sleep 30
- adb shell input keyevent 82
override:
# - ./gradlew clean assemble
# This will run the tests:
- ./gradlew assemble connectedDebugAndroidTest -PdisablePreDex --console=plain --info
post:
- cp -r app/build/outputs $CIRCLE_ARTIFACTS
- cp -r app/build/outputs/androidTest-results/connected/ $CIRCLE_TEST_REPORTS
gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
/gradle/wrapper does contain gradle-wrapper.jar
A solution is on the circleCI forum.
You have to add execution right like the following : chmod +x gradlew
It works for me.
Here is an example of my circleci.yml :
machine:
java:
version: oraclejdk8
dependencies:
override:
- chmod +x gradlew
- ./gradlew dependencies
test:
override:
- chmod +x grailsw
- ./grailsw test-app --non-interactive
post:
- mkdir -p $CIRCLE_TEST_REPORTS/junit/
- find . -type f -regex ".*/target/test-reports/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;

Categories

Resources