From 7975a6c2233289a815de189a0fb6b7f9816c90bd Mon Sep 17 00:00:00 2001 From: nikk-nikaznan Date: Tue, 3 Dec 2024 10:51:43 +0000 Subject: [PATCH 01/33] add title and just year as x-axis label --- episodes/14-looping-data-sets.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/episodes/14-looping-data-sets.md b/episodes/14-looping-data-sets.md index 94d7ddbe4..7a22bbeab 100644 --- a/episodes/14-looping-data-sets.md +++ b/episodes/14-looping-data-sets.md @@ -192,12 +192,11 @@ to either filter out those columns or tell pandas to ignore them. This solution builds a useful legend by using the [string `split` method][split-method] to extract the `region` from the path 'data/gapminder\_gdp\_a\_specific\_region.csv'. -```python -import glob +```import glob import pandas as pd import matplotlib.pyplot as plt fig, ax = plt.subplots(1,1) -for filename in glob.glob('data/gapminder_gdp*.csv'): +for filename in glob.glob('/Users/nikkhadijahnikaznan/Downloads/data/gapminder_gdp*.csv'): dataframe = pd.read_csv(filename) # extract from the filename, expected to be in the format 'data/gapminder_gdp_.csv'. # we will split the string using the split method and `_` as our separator, @@ -207,13 +206,21 @@ for filename in glob.glob('data/gapminder_gdp*.csv'): # convenient abstractions for working with filesystem paths and could solve this as well: # from pathlib import Path # region = Path(filename).stem.split('_')[-1] - region = filename.split('_')[-1][:-4] + region = filename.split('_')[-1][:-4] + # extract the years from the columns of the dataframe + headings = dataframe.columns[1:] + years = headings.str.split('_').str.get(1) # pandas raises errors when it encounters non-numeric columns in a dataframe computation # but we can tell pandas to ignore them with the `numeric_only` parameter dataframe.mean(numeric_only=True).plot(ax=ax, label=region) # NOTE: another way of doing this selects just the columns with gdp in their name using the filter method # dataframe.filter(like="gdp").mean().plot(ax=ax, label=region) - +# set the title and labels +ax.set_title('GDP Per Capita for Regions Over Time') +ax.set_xticks(range(len(years))) +ax.set_xticklabels(years) +ax.set_xlabel('Year') +plt.tight_layout() plt.legend() plt.show() ``` From 2c5f20eadbafa1ef3ead51a6fa9fcec42114c545 Mon Sep 17 00:00:00 2001 From: nikk-nikaznan Date: Tue, 3 Dec 2024 10:53:54 +0000 Subject: [PATCH 02/33] cleaned up --- episodes/14-looping-data-sets.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/episodes/14-looping-data-sets.md b/episodes/14-looping-data-sets.md index 7a22bbeab..1a7af685c 100644 --- a/episodes/14-looping-data-sets.md +++ b/episodes/14-looping-data-sets.md @@ -192,11 +192,12 @@ to either filter out those columns or tell pandas to ignore them. This solution builds a useful legend by using the [string `split` method][split-method] to extract the `region` from the path 'data/gapminder\_gdp\_a\_specific\_region.csv'. -```import glob +```python +import glob import pandas as pd import matplotlib.pyplot as plt fig, ax = plt.subplots(1,1) -for filename in glob.glob('/Users/nikkhadijahnikaznan/Downloads/data/gapminder_gdp*.csv'): +for filename in glob.glob('data/gapminder_gdp*.csv'): dataframe = pd.read_csv(filename) # extract from the filename, expected to be in the format 'data/gapminder_gdp_.csv'. # we will split the string using the split method and `_` as our separator, From ee96ad3047997d992e1efd356000a80779047d33 Mon Sep 17 00:00:00 2001 From: maneesha <829690+maneesha@users.noreply.github.com> Date: Tue, 10 Dec 2024 07:15:10 -0500 Subject: [PATCH 03/33] Update CoC links to new handbook --- CODE_OF_CONDUCT.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f19b80495..4153dd42f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -9,5 +9,5 @@ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by following our [reporting guidelines][coc-reporting]. -[coc-reporting]: https://docs.carpentries.org/topic_folders/policies/incident-reporting.html -[coc]: https://docs.carpentries.org/topic_folders/policies/code-of-conduct.html +[coc-reporting]: https://docs.carpentries.org/policies/coc/incident-reporting.html +[coc]: https://docs.carpentries.org/policies/coc/ From b131278713ac1c44ff148c7fdc20a1935eb76256 Mon Sep 17 00:00:00 2001 From: maneesha <829690+maneesha@users.noreply.github.com> Date: Tue, 10 Dec 2024 07:15:59 -0500 Subject: [PATCH 04/33] update CoC links --- links.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/links.md b/links.md index f8e1e4a05..b907142e1 100644 --- a/links.md +++ b/links.md @@ -1,8 +1,8 @@ [cc-by-human]: https://creativecommons.org/licenses/by/4.0/ [cc-by-legal]: https://creativecommons.org/licenses/by/4.0/legalcode [ci]: https://communityin.org/ -[coc-reporting]: https://docs.carpentries.org/topic_folders/policies/incident-reporting.html -[coc]: https://docs.carpentries.org/topic_folders/policies/code-of-conduct.html +[coc-reporting]: https://docs.carpentries.org/policies/coc/incident-reporting.html +[coc]: https://docs.carpentries.org/policies/coc/ [concept-maps]: https://carpentries.github.io/instructor-training/05-memory/ [contrib-covenant]: https://contributor-covenant.org/ [cran-checkpoint]: https://cran.r-project.org/package=checkpoint From a89b1bb1e6dbd96a1641bdb0dcb85b16de838c65 Mon Sep 17 00:00:00 2001 From: maneesha <829690+maneesha@users.noreply.github.com> Date: Fri, 31 Jan 2025 16:28:14 -0500 Subject: [PATCH 05/33] rm reference to CI FSP --- LICENSE.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 7632871ff..fd13a1b63 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -69,11 +69,11 @@ SOFTWARE. ## Trademark "The Carpentries", "Software Carpentry", "Data Carpentry", and "Library -Carpentry" and their respective logos are registered trademarks of [Community -Initiatives][ci]. +Carpentry" and their respective logos are registered trademarks of +[The Carpentries, Inc.][carpentries]. [cc-by-human]: https://creativecommons.org/licenses/by/4.0/ [cc-by-legal]: https://creativecommons.org/licenses/by/4.0/legalcode [mit-license]: https://opensource.org/licenses/mit-license.html -[ci]: https://communityin.org/ +[carpentries]: https://carpentries.org [osi]: https://opensource.org From 34e1127eaf21dff1327ee89dbbd2f70e03422524 Mon Sep 17 00:00:00 2001 From: Jan Simson Date: Fri, 28 Feb 2025 13:51:58 +0100 Subject: [PATCH 06/33] Add first draft of learner-profiles --- profiles/learner-profiles.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/profiles/learner-profiles.md b/profiles/learner-profiles.md index 434e335aa..feaee3ca9 100644 --- a/profiles/learner-profiles.md +++ b/profiles/learner-profiles.md @@ -1,5 +1,21 @@ --- -title: FIXME +title: Learner Profiles --- -This is a placeholder file. Please add content here. +## Carla Correlation + +Maria is a social scientist with a PhD in Sociology and 2 years of research experience. She primarily uses SPSS for data analysis and has extensive domain knowledge in her field. Her department is increasingly adopting Python for research, but she has never written code beyond SPSS syntax files. + +This course will teach Maria the Python fundamentals needed to analyze cross-country demographic data for her paper on education outcomes. She's particularly interested in the data importing, visualization, and statistical analysis episodes. After completing the workshop, Maria will be able to independently import CSV files, perform basic data cleaning, create simple visualizations of demographic trends, and produce summary statistics from tabular data without relying on colleagues for code support. + +## Jim JIT + +Jim teaches biology and environmental science to high school students with 12 years of teaching experience. While comfortable with educational technology, he has no programming experience. He wants to incorporate real-world data analysis into his curriculum to engage students with current global challenges and teach them valuable skills, but is concerned about simplifying complex concepts for teenage students and designing activities that can fit within 45-minute class periods. + +This course will provide Jim with the foundational Python knowledge needed to create data science activities for his classroom. He's most interested in the basic programming concepts and data visualization aspects that he can adapt for student use. After the workshop, Jim will be able to create simple, guided activities using the Gapminder dataset that connect scientific concepts to real-world data, demonstrate basic data visualization techniques to his students, and confidently answer questions about the code. He'll develop lesson materials that help prepare his students for college-level coursework while teaching both scientific concepts and analytical skills. + +## Peter Pandas + +Peter is a medical student conducting research during a gap year before residency. With an MD degree completed and some undergraduate coursework in statistics, they need to analyze global health data for a project on childhood vaccination rates. Despite being tech-savvy with other digital tools, Peter has never programmed before and has very limited time available between clinical responsibilities. They struggle with abstract concepts and prefer learning through concrete examples directly applicable to their work. + +This course will teach Peter the essential Python skills needed to analyze datasets for their research projects. They're particularly interested in the data filtering and visualization episodes that will allow them to extract meaningful patterns from data. After completing the workshop, Peter will be able to import their research data, filter it based on specific criteria like geographic region or time period and create visualizations illustrating relationships. For repeated steps, they will be able to write functions to not have to repeat themselves. From 18c0edde67896cc7ec4dabbe2840bbe8593dfb6a Mon Sep 17 00:00:00 2001 From: Jan Simson Date: Mon, 3 Mar 2025 15:10:22 +0100 Subject: [PATCH 07/33] Fix inconsistent use of name in one of the learner profiles --- profiles/learner-profiles.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/profiles/learner-profiles.md b/profiles/learner-profiles.md index feaee3ca9..da2bedc98 100644 --- a/profiles/learner-profiles.md +++ b/profiles/learner-profiles.md @@ -4,9 +4,9 @@ title: Learner Profiles ## Carla Correlation -Maria is a social scientist with a PhD in Sociology and 2 years of research experience. She primarily uses SPSS for data analysis and has extensive domain knowledge in her field. Her department is increasingly adopting Python for research, but she has never written code beyond SPSS syntax files. +Carla is a social scientist with a PhD in Sociology and 2 years of research experience. She primarily uses SPSS for data analysis and has extensive domain knowledge in her field. Her department is increasingly adopting Python for research, but she has never written code beyond SPSS syntax files. -This course will teach Maria the Python fundamentals needed to analyze cross-country demographic data for her paper on education outcomes. She's particularly interested in the data importing, visualization, and statistical analysis episodes. After completing the workshop, Maria will be able to independently import CSV files, perform basic data cleaning, create simple visualizations of demographic trends, and produce summary statistics from tabular data without relying on colleagues for code support. +This course will teach Carla the Python fundamentals needed to analyze cross-country demographic data for her paper on education outcomes. She's particularly interested in the data importing, visualization, and statistical analysis episodes. After completing the workshop, Carla will be able to independently import CSV files, perform basic data cleaning, create simple visualizations of demographic trends, and produce summary statistics from tabular data without relying on colleagues for code support. ## Jim JIT From efd23d5d8b55eac447b8b15e474bdcaf3ca388f0 Mon Sep 17 00:00:00 2001 From: Jan Simson Date: Mon, 3 Mar 2025 15:15:38 +0100 Subject: [PATCH 08/33] Add a new paragraph to Carla's learner profile about this workshop being the first step on the journey. --- profiles/learner-profiles.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/profiles/learner-profiles.md b/profiles/learner-profiles.md index da2bedc98..a6cbe81b4 100644 --- a/profiles/learner-profiles.md +++ b/profiles/learner-profiles.md @@ -8,6 +8,8 @@ Carla is a social scientist with a PhD in Sociology and 2 years of research expe This course will teach Carla the Python fundamentals needed to analyze cross-country demographic data for her paper on education outcomes. She's particularly interested in the data importing, visualization, and statistical analysis episodes. After completing the workshop, Carla will be able to independently import CSV files, perform basic data cleaning, create simple visualizations of demographic trends, and produce summary statistics from tabular data without relying on colleagues for code support. +This workshop serves as a first step in Carla's Python journey, providing her with a coherent mental model of programming and data visualization that will form the foundation of her future learning. She will be equipped to ask well informed questions about programming in the future, recognize what's possible with Python, and independently explore resources to expand her skills. + ## Jim JIT Jim teaches biology and environmental science to high school students with 12 years of teaching experience. While comfortable with educational technology, he has no programming experience. He wants to incorporate real-world data analysis into his curriculum to engage students with current global challenges and teach them valuable skills, but is concerned about simplifying complex concepts for teenage students and designing activities that can fit within 45-minute class periods. From 02aa33f382b65aeb290c8c7dd5c2b77f06d6ff5b Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Thu, 13 Mar 2025 18:05:39 +0100 Subject: [PATCH 09/33] mention internet searches and talking to people as ways to get help --- episodes/04-built-in.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index e11685be7..7fbfb0c3a 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -259,6 +259,21 @@ NameError: name 'aege' is not defined - Fix syntax errors by reading the source and runtime errors by tracing execution. +## Other ways to get help +There are several other ways that people often get help when they are stuck with their Python code. +Perhaps the most common is to search the internet: +paste the last line of your error message into your favourite search engine +and you will usually find several examples where other people have encountered the same problem and came looking for help. +You can take a similar approach if you want to find out how to achieve something with Python that you have not done before. +[StackOverflow](https://stackoverflow.com/questions) can be particularly helpful for this: answers to questions are presented as a ranked thread ordered according to how useful other users found them to be. +**Take care:** copying and pasting code written by somebody else is risky unless you understand exactly what it is doing! + +We also encourage you to get help when you are stuck by asking somebody "in the real world". +If you have a colleague or friend with more expertise in Python than you have, show them the problem you are having and ask them for help. +Sometimes, simply the act of formulating your question can help you to identify what is going wrong. +This is known as ["rubber duck debugging"](https://en.wikipedia.org/wiki/Rubber_duck_debugging) among programmers. + + ::::::::::::::::::::::::::::::::::::::: challenge ## What Happens When From ab87f14a323147f98dfa29fd587ed6fd5b470fab Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Thu, 13 Mar 2025 19:20:59 +0100 Subject: [PATCH 10/33] convert to bullet points --- episodes/04-built-in.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index 7fbfb0c3a..2415eb2b6 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -261,17 +261,16 @@ NameError: name 'aege' is not defined ## Other ways to get help There are several other ways that people often get help when they are stuck with their Python code. -Perhaps the most common is to search the internet: -paste the last line of your error message into your favourite search engine -and you will usually find several examples where other people have encountered the same problem and came looking for help. -You can take a similar approach if you want to find out how to achieve something with Python that you have not done before. -[StackOverflow](https://stackoverflow.com/questions) can be particularly helpful for this: answers to questions are presented as a ranked thread ordered according to how useful other users found them to be. -**Take care:** copying and pasting code written by somebody else is risky unless you understand exactly what it is doing! - -We also encourage you to get help when you are stuck by asking somebody "in the real world". -If you have a colleague or friend with more expertise in Python than you have, show them the problem you are having and ask them for help. -Sometimes, simply the act of formulating your question can help you to identify what is going wrong. -This is known as ["rubber duck debugging"](https://en.wikipedia.org/wiki/Rubber_duck_debugging) among programmers. + +* Search the internet: + paste the last line of your error message or the word "python" and a short description of what you want to do into your favourite search engine + and you will usually find several examples where other people have encountered the same problem and came looking for help. +* [StackOverflow](https://stackoverflow.com/questions) can be particularly helpful for this: answers to questions are presented as a ranked thread ordered according to how useful other users found them to be. +* **Take care:** copying and pasting code written by somebody else is risky unless you understand exactly what it is doing! +* ask somebody "in the real world". + If you have a colleague or friend with more expertise in Python than you have, show them the problem you are having and ask them for help. +* Sometimes, simply the act of formulating your question can help you to identify what is going wrong. + This is known as ["rubber duck debugging"](https://en.wikipedia.org/wiki/Rubber_duck_debugging) among programmers. ::::::::::::::::::::::::::::::::::::::: challenge From b35095f607dc9835e153b84b3c7197050629dac1 Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Thu, 13 Mar 2025 19:21:35 +0100 Subject: [PATCH 11/33] first draft genAI subsection --- episodes/04-built-in.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index 2415eb2b6..35a806fa3 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -272,6 +272,34 @@ There are several other ways that people often get help when they are stuck with * Sometimes, simply the act of formulating your question can help you to identify what is going wrong. This is known as ["rubber duck debugging"](https://en.wikipedia.org/wiki/Rubber_duck_debugging) among programmers. +### Generative AI + +It is increasingly common for people to use _generative AI_ chatbots such as ChatGPT to get help while coding. +Once again, you will probably receive some useful guidance by presenting your error message to the chatbot and asking it what went wrong. +However, the way this help is provided by the chatbot is different. +Answers on Stackoverflow have (probably) been given by a human as a direct response to the question asked. +But generative AI chatbots, which are based on an advanced statistical model, respond by generating the _most likely_ sequence of text that would follow the prompt they are given. + +In many cases, these responses will be as accurate as those you could find online, but responses from a chatbot can and often do include errors. +Just as with an answer found on the internet, you need the knowledge and skills to be able to understand these responses, to judge whether or not they are accurate, and to fix any errors in the code it offers you. + +In addition to asking for help, programmers use generative AI tools to generate code from scratch, extend, improve and reorganise existing code, translate code between programming languages, figure out what terms to use in a search of the internet, and more. +However, there are drawbacks that you should be aware of. + +The models used by these tools have been "trained" on very large volumes of data, much of it taken from the internet, and the responses they produce reflect that training data. +Very large amounts of energy was consumed when training most of the models in widespread use and many people are concerned about the environmental cost of this. +Concerns also exist about the way the data for this training was obtained, with questions raised about whether the developers had permission to use it. +Other ethical concerns have also been raised, such as reports that workers were exploited during the training process. + +**We recommend that you avoid getting help from generative AI while you learn to code** for several reasons: + +1. For most problems you will encounter at this stage, help and answers can be easily found by searching the internet. +2. The foundational knowledge and skills you will learn in this lesson are essential for you to be able to fix your own programs and any code you receive from online help or a generative AI chatbot. + If you choose to use these tools in the future, the expertise you gain from learning and practising these fundamentals on your own will help you use them more effectively. +3. As you start out with programming, you will make extremely common mistakes that have been made by everybody else who learned to program before you. + Since these mistakes and the questions you are likely to have at this stage are common, they are better represented in the training data of generative AI tools than other, more specialised problems and tasks. + This means that a generative AI chatbot is _more likely to produce accurate responses_ to questions that novices ask, which could give you a false impression of how reliable they will be when you are ready to do things that are more advanced. + ::::::::::::::::::::::::::::::::::::::: challenge From 79423bebc728cb8f6b56a9f9dac0b7384b677bac Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Fri, 14 Mar 2025 10:29:29 +0100 Subject: [PATCH 12/33] minor polishes for more positive language --- episodes/04-built-in.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index 35a806fa3..a6cb9c4a6 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -296,8 +296,8 @@ Other ethical concerns have also been raised, such as reports that workers were 1. For most problems you will encounter at this stage, help and answers can be easily found by searching the internet. 2. The foundational knowledge and skills you will learn in this lesson are essential for you to be able to fix your own programs and any code you receive from online help or a generative AI chatbot. If you choose to use these tools in the future, the expertise you gain from learning and practising these fundamentals on your own will help you use them more effectively. -3. As you start out with programming, you will make extremely common mistakes that have been made by everybody else who learned to program before you. - Since these mistakes and the questions you are likely to have at this stage are common, they are better represented in the training data of generative AI tools than other, more specialised problems and tasks. +3. As you start out with programming, the mistakes you make will be the kinds that have also been made -- and overcome! -- by everybody else who learned to program before you. + Since these mistakes and the questions you are likely to have at this stage are common, they are also better represented than other, more specialised problems and tasks in the data that was used to train generative AI tools. This means that a generative AI chatbot is _more likely to produce accurate responses_ to questions that novices ask, which could give you a false impression of how reliable they will be when you are ready to do things that are more advanced. From 8b5af274f0299cb536fedec6f3a6003c03f8e6b4 Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Fri, 14 Mar 2025 11:02:22 +0100 Subject: [PATCH 13/33] first draft guidance for Instructors --- episodes/04-built-in.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index a6cb9c4a6..072c2a316 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -274,6 +274,21 @@ There are several other ways that people often get help when they are stuck with ### Generative AI +::::::::::::::::::::::::::::: instructor + +### Choose how to teach this section +The section on generative AI is intended to be concise but Instructors may choose to devote more time to the topic in a workshop. +Depending on your own level of experience and comfort with talking about and using these tools, you could choose to do any of the following: + +* Explain how large language models work and are trained, and/or the difference between generative AI, other forms of AI that currently exist, and the concept of artificial general intelligence. +* Demonstrate how you recommend that learners use generative AI. +* Discuss the ethical concerns listed below, as well as others that you are aware of, to help learners make an informed choice about whether or not to use generative AI tools. + +This is a fast-moving technology. +If you are preparing to teach this section and you feel it has become outdated, please open an issue on the lesson repository to let the Maintainers know and/or a pull request to suggest updates and improvements. + +:::::::::::::::::::::::::::::::::::::::: + It is increasingly common for people to use _generative AI_ chatbots such as ChatGPT to get help while coding. Once again, you will probably receive some useful guidance by presenting your error message to the chatbot and asking it what went wrong. However, the way this help is provided by the chatbot is different. From 18ad693d98aafce555b445bbc982aaa7f55e4f96 Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Fri, 14 Mar 2025 11:32:27 +0100 Subject: [PATCH 14/33] more polishing --- episodes/04-built-in.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index 072c2a316..37b56ce1d 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -293,15 +293,16 @@ It is increasingly common for people to use _generative AI_ chatbots such as Cha Once again, you will probably receive some useful guidance by presenting your error message to the chatbot and asking it what went wrong. However, the way this help is provided by the chatbot is different. Answers on Stackoverflow have (probably) been given by a human as a direct response to the question asked. -But generative AI chatbots, which are based on an advanced statistical model, respond by generating the _most likely_ sequence of text that would follow the prompt they are given. +But generative AI chatbots, which are built on an advanced statistical model based on oberservations or which combinations of words tend to appear together, respond by generating the _most likely_ sequence of text that would follow the prompt they are given. In many cases, these responses will be as accurate as those you could find online, but responses from a chatbot can and often do include errors. -Just as with an answer found on the internet, you need the knowledge and skills to be able to understand these responses, to judge whether or not they are accurate, and to fix any errors in the code it offers you. +Just as with an answer found on the internet, you should **take care** to ensure you understand what any code the tool has suggested is going to do when it is run. +You will need knowledge and skills to be able to understand the responses you get from these tools, to judge whether or not they are accurate, and to fix any errors in the code it may offer you. In addition to asking for help, programmers use generative AI tools to generate code from scratch, extend, improve and reorganise existing code, translate code between programming languages, figure out what terms to use in a search of the internet, and more. However, there are drawbacks that you should be aware of. -The models used by these tools have been "trained" on very large volumes of data, much of it taken from the internet, and the responses they produce reflect that training data. +The models used by these tools have been "trained" on enormous volumes of data, much of it taken from the internet, and the responses they produce reflect that training data. Very large amounts of energy was consumed when training most of the models in widespread use and many people are concerned about the environmental cost of this. Concerns also exist about the way the data for this training was obtained, with questions raised about whether the developers had permission to use it. Other ethical concerns have also been raised, such as reports that workers were exploited during the training process. From a375c9a11449330e9e762efb9d770562091b9c10 Mon Sep 17 00:00:00 2001 From: Olav Vahtras Date: Fri, 14 Mar 2025 14:31:29 +0100 Subject: [PATCH 15/33] Revert "Add content on generative AI" --- episodes/04-built-in.md | 58 ----------------------------------------- 1 file changed, 58 deletions(-) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index 37b56ce1d..e11685be7 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -259,64 +259,6 @@ NameError: name 'aege' is not defined - Fix syntax errors by reading the source and runtime errors by tracing execution. -## Other ways to get help -There are several other ways that people often get help when they are stuck with their Python code. - -* Search the internet: - paste the last line of your error message or the word "python" and a short description of what you want to do into your favourite search engine - and you will usually find several examples where other people have encountered the same problem and came looking for help. -* [StackOverflow](https://stackoverflow.com/questions) can be particularly helpful for this: answers to questions are presented as a ranked thread ordered according to how useful other users found them to be. -* **Take care:** copying and pasting code written by somebody else is risky unless you understand exactly what it is doing! -* ask somebody "in the real world". - If you have a colleague or friend with more expertise in Python than you have, show them the problem you are having and ask them for help. -* Sometimes, simply the act of formulating your question can help you to identify what is going wrong. - This is known as ["rubber duck debugging"](https://en.wikipedia.org/wiki/Rubber_duck_debugging) among programmers. - -### Generative AI - -::::::::::::::::::::::::::::: instructor - -### Choose how to teach this section -The section on generative AI is intended to be concise but Instructors may choose to devote more time to the topic in a workshop. -Depending on your own level of experience and comfort with talking about and using these tools, you could choose to do any of the following: - -* Explain how large language models work and are trained, and/or the difference between generative AI, other forms of AI that currently exist, and the concept of artificial general intelligence. -* Demonstrate how you recommend that learners use generative AI. -* Discuss the ethical concerns listed below, as well as others that you are aware of, to help learners make an informed choice about whether or not to use generative AI tools. - -This is a fast-moving technology. -If you are preparing to teach this section and you feel it has become outdated, please open an issue on the lesson repository to let the Maintainers know and/or a pull request to suggest updates and improvements. - -:::::::::::::::::::::::::::::::::::::::: - -It is increasingly common for people to use _generative AI_ chatbots such as ChatGPT to get help while coding. -Once again, you will probably receive some useful guidance by presenting your error message to the chatbot and asking it what went wrong. -However, the way this help is provided by the chatbot is different. -Answers on Stackoverflow have (probably) been given by a human as a direct response to the question asked. -But generative AI chatbots, which are built on an advanced statistical model based on oberservations or which combinations of words tend to appear together, respond by generating the _most likely_ sequence of text that would follow the prompt they are given. - -In many cases, these responses will be as accurate as those you could find online, but responses from a chatbot can and often do include errors. -Just as with an answer found on the internet, you should **take care** to ensure you understand what any code the tool has suggested is going to do when it is run. -You will need knowledge and skills to be able to understand the responses you get from these tools, to judge whether or not they are accurate, and to fix any errors in the code it may offer you. - -In addition to asking for help, programmers use generative AI tools to generate code from scratch, extend, improve and reorganise existing code, translate code between programming languages, figure out what terms to use in a search of the internet, and more. -However, there are drawbacks that you should be aware of. - -The models used by these tools have been "trained" on enormous volumes of data, much of it taken from the internet, and the responses they produce reflect that training data. -Very large amounts of energy was consumed when training most of the models in widespread use and many people are concerned about the environmental cost of this. -Concerns also exist about the way the data for this training was obtained, with questions raised about whether the developers had permission to use it. -Other ethical concerns have also been raised, such as reports that workers were exploited during the training process. - -**We recommend that you avoid getting help from generative AI while you learn to code** for several reasons: - -1. For most problems you will encounter at this stage, help and answers can be easily found by searching the internet. -2. The foundational knowledge and skills you will learn in this lesson are essential for you to be able to fix your own programs and any code you receive from online help or a generative AI chatbot. - If you choose to use these tools in the future, the expertise you gain from learning and practising these fundamentals on your own will help you use them more effectively. -3. As you start out with programming, the mistakes you make will be the kinds that have also been made -- and overcome! -- by everybody else who learned to program before you. - Since these mistakes and the questions you are likely to have at this stage are common, they are also better represented than other, more specialised problems and tasks in the data that was used to train generative AI tools. - This means that a generative AI chatbot is _more likely to produce accurate responses_ to questions that novices ask, which could give you a false impression of how reliable they will be when you are ready to do things that are more advanced. - - ::::::::::::::::::::::::::::::::::::::: challenge ## What Happens When From ba82693b9573e5d05ab1a6aadada01394202dd71 Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Fri, 14 Mar 2025 14:58:12 +0100 Subject: [PATCH 16/33] re-add genAI content --- episodes/04-built-in.md | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index e11685be7..072c2a316 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -259,6 +259,63 @@ NameError: name 'aege' is not defined - Fix syntax errors by reading the source and runtime errors by tracing execution. +## Other ways to get help +There are several other ways that people often get help when they are stuck with their Python code. + +* Search the internet: + paste the last line of your error message or the word "python" and a short description of what you want to do into your favourite search engine + and you will usually find several examples where other people have encountered the same problem and came looking for help. +* [StackOverflow](https://stackoverflow.com/questions) can be particularly helpful for this: answers to questions are presented as a ranked thread ordered according to how useful other users found them to be. +* **Take care:** copying and pasting code written by somebody else is risky unless you understand exactly what it is doing! +* ask somebody "in the real world". + If you have a colleague or friend with more expertise in Python than you have, show them the problem you are having and ask them for help. +* Sometimes, simply the act of formulating your question can help you to identify what is going wrong. + This is known as ["rubber duck debugging"](https://en.wikipedia.org/wiki/Rubber_duck_debugging) among programmers. + +### Generative AI + +::::::::::::::::::::::::::::: instructor + +### Choose how to teach this section +The section on generative AI is intended to be concise but Instructors may choose to devote more time to the topic in a workshop. +Depending on your own level of experience and comfort with talking about and using these tools, you could choose to do any of the following: + +* Explain how large language models work and are trained, and/or the difference between generative AI, other forms of AI that currently exist, and the concept of artificial general intelligence. +* Demonstrate how you recommend that learners use generative AI. +* Discuss the ethical concerns listed below, as well as others that you are aware of, to help learners make an informed choice about whether or not to use generative AI tools. + +This is a fast-moving technology. +If you are preparing to teach this section and you feel it has become outdated, please open an issue on the lesson repository to let the Maintainers know and/or a pull request to suggest updates and improvements. + +:::::::::::::::::::::::::::::::::::::::: + +It is increasingly common for people to use _generative AI_ chatbots such as ChatGPT to get help while coding. +Once again, you will probably receive some useful guidance by presenting your error message to the chatbot and asking it what went wrong. +However, the way this help is provided by the chatbot is different. +Answers on Stackoverflow have (probably) been given by a human as a direct response to the question asked. +But generative AI chatbots, which are based on an advanced statistical model, respond by generating the _most likely_ sequence of text that would follow the prompt they are given. + +In many cases, these responses will be as accurate as those you could find online, but responses from a chatbot can and often do include errors. +Just as with an answer found on the internet, you need the knowledge and skills to be able to understand these responses, to judge whether or not they are accurate, and to fix any errors in the code it offers you. + +In addition to asking for help, programmers use generative AI tools to generate code from scratch, extend, improve and reorganise existing code, translate code between programming languages, figure out what terms to use in a search of the internet, and more. +However, there are drawbacks that you should be aware of. + +The models used by these tools have been "trained" on very large volumes of data, much of it taken from the internet, and the responses they produce reflect that training data. +Very large amounts of energy was consumed when training most of the models in widespread use and many people are concerned about the environmental cost of this. +Concerns also exist about the way the data for this training was obtained, with questions raised about whether the developers had permission to use it. +Other ethical concerns have also been raised, such as reports that workers were exploited during the training process. + +**We recommend that you avoid getting help from generative AI while you learn to code** for several reasons: + +1. For most problems you will encounter at this stage, help and answers can be easily found by searching the internet. +2. The foundational knowledge and skills you will learn in this lesson are essential for you to be able to fix your own programs and any code you receive from online help or a generative AI chatbot. + If you choose to use these tools in the future, the expertise you gain from learning and practising these fundamentals on your own will help you use them more effectively. +3. As you start out with programming, the mistakes you make will be the kinds that have also been made -- and overcome! -- by everybody else who learned to program before you. + Since these mistakes and the questions you are likely to have at this stage are common, they are also better represented than other, more specialised problems and tasks in the data that was used to train generative AI tools. + This means that a generative AI chatbot is _more likely to produce accurate responses_ to questions that novices ask, which could give you a false impression of how reliable they will be when you are ready to do things that are more advanced. + + ::::::::::::::::::::::::::::::::::::::: challenge ## What Happens When From 0d10b5399fe4c5622c2704e3576c44a8923828d9 Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Tue, 18 Mar 2025 11:20:55 +0100 Subject: [PATCH 17/33] Apply suggestions from review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Pérez-Suárez Co-authored-by: Sarah Brown --- episodes/04-built-in.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index 072c2a316..fd6734f4d 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -265,8 +265,8 @@ There are several other ways that people often get help when they are stuck with * Search the internet: paste the last line of your error message or the word "python" and a short description of what you want to do into your favourite search engine and you will usually find several examples where other people have encountered the same problem and came looking for help. -* [StackOverflow](https://stackoverflow.com/questions) can be particularly helpful for this: answers to questions are presented as a ranked thread ordered according to how useful other users found them to be. -* **Take care:** copying and pasting code written by somebody else is risky unless you understand exactly what it is doing! + * [StackOverflow](https://stackoverflow.com/questions) can be particularly helpful for this: answers to questions are presented as a ranked thread ordered according to how useful other users found them to be. + * **Take care:** copying and pasting code written by somebody else is risky unless you understand exactly what it is doing! * ask somebody "in the real world". If you have a colleague or friend with more expertise in Python than you have, show them the problem you are having and ask them for help. * Sometimes, simply the act of formulating your question can help you to identify what is going wrong. @@ -298,7 +298,7 @@ But generative AI chatbots, which are based on an advanced statistical model, re In many cases, these responses will be as accurate as those you could find online, but responses from a chatbot can and often do include errors. Just as with an answer found on the internet, you need the knowledge and skills to be able to understand these responses, to judge whether or not they are accurate, and to fix any errors in the code it offers you. -In addition to asking for help, programmers use generative AI tools to generate code from scratch, extend, improve and reorganise existing code, translate code between programming languages, figure out what terms to use in a search of the internet, and more. +In addition to asking for help, programmers can use generative AI tools to generate code from scratch; extend, improve and reorganise existing code; translate code between programming languages; figure out what terms to use in a search of the internet; and more. However, there are drawbacks that you should be aware of. The models used by these tools have been "trained" on very large volumes of data, much of it taken from the internet, and the responses they produce reflect that training data. @@ -306,10 +306,10 @@ Very large amounts of energy was consumed when training most of the models in wi Concerns also exist about the way the data for this training was obtained, with questions raised about whether the developers had permission to use it. Other ethical concerns have also been raised, such as reports that workers were exploited during the training process. -**We recommend that you avoid getting help from generative AI while you learn to code** for several reasons: +**We recommend that you avoid getting help from generative AI during the workshop** for several reasons: -1. For most problems you will encounter at this stage, help and answers can be easily found by searching the internet. -2. The foundational knowledge and skills you will learn in this lesson are essential for you to be able to fix your own programs and any code you receive from online help or a generative AI chatbot. +1. For most problems you will encounter at this stage, help and answers can be found among the first results returned by searching the internet. +2. The foundational knowledge and skills you will learn in this lesson by writing and fixing your own programs are essential to be able to evaluate the correctness and safety of any code you receive from online help or a generative AI chatbot. If you choose to use these tools in the future, the expertise you gain from learning and practising these fundamentals on your own will help you use them more effectively. 3. As you start out with programming, the mistakes you make will be the kinds that have also been made -- and overcome! -- by everybody else who learned to program before you. Since these mistakes and the questions you are likely to have at this stage are common, they are also better represented than other, more specialised problems and tasks in the data that was used to train generative AI tools. From 06561b9b95f539285c0bd15d55b1de1a4f7c5d4c Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Tue, 18 Mar 2025 11:22:08 +0100 Subject: [PATCH 18/33] formulating -> articulating --- episodes/04-built-in.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index fd6734f4d..74b57302b 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -269,7 +269,7 @@ There are several other ways that people often get help when they are stuck with * **Take care:** copying and pasting code written by somebody else is risky unless you understand exactly what it is doing! * ask somebody "in the real world". If you have a colleague or friend with more expertise in Python than you have, show them the problem you are having and ask them for help. -* Sometimes, simply the act of formulating your question can help you to identify what is going wrong. +* Sometimes, the act of articulating your question can help you to identify what is going wrong. This is known as ["rubber duck debugging"](https://en.wikipedia.org/wiki/Rubber_duck_debugging) among programmers. ### Generative AI From d55c2fee702329c48c6a34b0e80f18835101384b Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Tue, 18 Mar 2025 11:47:30 +0100 Subject: [PATCH 19/33] language polishes Co-authored-by: Federica Gazzelloni (she/her) <61802414+Fgazzelloni@users.noreply.github.com> --- episodes/04-built-in.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index 74b57302b..893324fee 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -295,8 +295,9 @@ However, the way this help is provided by the chatbot is different. Answers on Stackoverflow have (probably) been given by a human as a direct response to the question asked. But generative AI chatbots, which are based on an advanced statistical model, respond by generating the _most likely_ sequence of text that would follow the prompt they are given. -In many cases, these responses will be as accurate as those you could find online, but responses from a chatbot can and often do include errors. -Just as with an answer found on the internet, you need the knowledge and skills to be able to understand these responses, to judge whether or not they are accurate, and to fix any errors in the code it offers you. +While responses from generative AI tools can often be helpful, they are not always reliable. +These tools sometimes generate plausible but incorrect or misleading information, so (just as with an answer found on the internet) it is essential to verify their accuracy. +You need the knowledge and skills to be able to understand these responses, to judge whether or not they are accurate, and to fix any errors in the code it offers you. In addition to asking for help, programmers can use generative AI tools to generate code from scratch; extend, improve and reorganise existing code; translate code between programming languages; figure out what terms to use in a search of the internet; and more. However, there are drawbacks that you should be aware of. From ef84a897a13f25a12ee38bdf24eeba6cbb6933d8 Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Wed, 19 Mar 2025 14:25:07 +0100 Subject: [PATCH 20/33] Apply suggestions from review Co-authored-by: Daniel McCloy --- episodes/04-built-in.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index 893324fee..d79880b94 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -290,9 +290,9 @@ If you are preparing to teach this section and you feel it has become outdated, :::::::::::::::::::::::::::::::::::::::: It is increasingly common for people to use _generative AI_ chatbots such as ChatGPT to get help while coding. -Once again, you will probably receive some useful guidance by presenting your error message to the chatbot and asking it what went wrong. +You will probably receive some useful guidance by presenting your error message to the chatbot and asking it what went wrong. However, the way this help is provided by the chatbot is different. -Answers on Stackoverflow have (probably) been given by a human as a direct response to the question asked. +Answers on StackOverflow have (probably) been given by a human as a direct response to the question asked. But generative AI chatbots, which are based on an advanced statistical model, respond by generating the _most likely_ sequence of text that would follow the prompt they are given. While responses from generative AI tools can often be helpful, they are not always reliable. @@ -302,7 +302,7 @@ You need the knowledge and skills to be able to understand these responses, to j In addition to asking for help, programmers can use generative AI tools to generate code from scratch; extend, improve and reorganise existing code; translate code between programming languages; figure out what terms to use in a search of the internet; and more. However, there are drawbacks that you should be aware of. -The models used by these tools have been "trained" on very large volumes of data, much of it taken from the internet, and the responses they produce reflect that training data. +The models used by these tools have been "trained" on very large volumes of data, much of it taken from the internet, and the responses they produce reflect that training data, and may recapitulate its inaccuracies or biases. Very large amounts of energy was consumed when training most of the models in widespread use and many people are concerned about the environmental cost of this. Concerns also exist about the way the data for this training was obtained, with questions raised about whether the developers had permission to use it. Other ethical concerns have also been raised, such as reports that workers were exploited during the training process. From 79cb5deae1db166e6e16949f2e62a6d459d65825 Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Wed, 19 Mar 2025 14:26:17 +0100 Subject: [PATCH 21/33] clarify which developers I am talking about --- episodes/04-built-in.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index d79880b94..fd124f1a8 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -304,7 +304,7 @@ However, there are drawbacks that you should be aware of. The models used by these tools have been "trained" on very large volumes of data, much of it taken from the internet, and the responses they produce reflect that training data, and may recapitulate its inaccuracies or biases. Very large amounts of energy was consumed when training most of the models in widespread use and many people are concerned about the environmental cost of this. -Concerns also exist about the way the data for this training was obtained, with questions raised about whether the developers had permission to use it. +Concerns also exist about the way the data for this training was obtained, with questions raised about whether the people developing the LLMs had permission to use it. Other ethical concerns have also been raised, such as reports that workers were exploited during the training process. **We recommend that you avoid getting help from generative AI during the workshop** for several reasons: From 812e4cd850029de7f160f973c8a5676a5a43de40 Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Fri, 28 Mar 2025 16:00:42 +0100 Subject: [PATCH 22/33] Apply suggestions from code review Co-authored-by: Daniel McCloy Co-authored-by: Sarah Brown --- episodes/04-built-in.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index fd124f1a8..a6d32906b 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -280,7 +280,7 @@ There are several other ways that people often get help when they are stuck with The section on generative AI is intended to be concise but Instructors may choose to devote more time to the topic in a workshop. Depending on your own level of experience and comfort with talking about and using these tools, you could choose to do any of the following: -* Explain how large language models work and are trained, and/or the difference between generative AI, other forms of AI that currently exist, and the concept of artificial general intelligence. +* Explain how large language models work and are trained, and/or the difference between generative AI, other forms of AI that currently exist, and the limits of what LLMs can do (e.g., they can't "reason"). * Demonstrate how you recommend that learners use generative AI. * Discuss the ethical concerns listed below, as well as others that you are aware of, to help learners make an informed choice about whether or not to use generative AI tools. @@ -303,7 +303,7 @@ In addition to asking for help, programmers can use generative AI tools to gener However, there are drawbacks that you should be aware of. The models used by these tools have been "trained" on very large volumes of data, much of it taken from the internet, and the responses they produce reflect that training data, and may recapitulate its inaccuracies or biases. -Very large amounts of energy was consumed when training most of the models in widespread use and many people are concerned about the environmental cost of this. +The environmental costs (energy and water use) of LLMs are a lot higher than other technologies, both during development (known as training) and when an individual user uses one (also called inference). For more information see the [AI Environmental Impact Primer](https://huggingface.co/blog/sasha/ai-environment-primer) developed researchers at HuggingFace, an AI hosting platform. Concerns also exist about the way the data for this training was obtained, with questions raised about whether the people developing the LLMs had permission to use it. Other ethical concerns have also been raised, such as reports that workers were exploited during the training process. From cf0f86e64a2ffc3e622effe2198df20426be6c59 Mon Sep 17 00:00:00 2001 From: Toby Hodges Date: Fri, 28 Mar 2025 16:01:15 +0100 Subject: [PATCH 23/33] typo fix --- episodes/04-built-in.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/episodes/04-built-in.md b/episodes/04-built-in.md index a6d32906b..f8f7463ae 100644 --- a/episodes/04-built-in.md +++ b/episodes/04-built-in.md @@ -303,7 +303,7 @@ In addition to asking for help, programmers can use generative AI tools to gener However, there are drawbacks that you should be aware of. The models used by these tools have been "trained" on very large volumes of data, much of it taken from the internet, and the responses they produce reflect that training data, and may recapitulate its inaccuracies or biases. -The environmental costs (energy and water use) of LLMs are a lot higher than other technologies, both during development (known as training) and when an individual user uses one (also called inference). For more information see the [AI Environmental Impact Primer](https://huggingface.co/blog/sasha/ai-environment-primer) developed researchers at HuggingFace, an AI hosting platform. +The environmental costs (energy and water use) of LLMs are a lot higher than other technologies, both during development (known as training) and when an individual user uses one (also called inference). For more information see the [AI Environmental Impact Primer](https://huggingface.co/blog/sasha/ai-environment-primer) developed by researchers at HuggingFace, an AI hosting platform. Concerns also exist about the way the data for this training was obtained, with questions raised about whether the people developing the LLMs had permission to use it. Other ethical concerns have also been raised, such as reports that workers were exploited during the training process. From 21894419d19902289b1e2c5172915d70beb6ede3 Mon Sep 17 00:00:00 2001 From: zkamvar Date: Tue, 13 May 2025 00:03:28 +0000 Subject: [PATCH 24/33] [actions] update sandpaper workflow to version 0.16.12 --- .github/workflows/README.md | 2 +- .github/workflows/sandpaper-version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 7076ddd9f..18ab76509 100755 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -43,7 +43,7 @@ This workflow does the following: #### Caching This workflow has two caches; one cache is for the lesson infrastructure and -the other is for the the lesson dependencies if the lesson contains rendered +the other is for the lesson dependencies if the lesson contains rendered content. These caches are invalidated by new versions of the infrastructure and the `renv.lock` file, respectively. If there is a problem with the cache, manual invaliation is necessary. You will need maintain access to the repository diff --git a/.github/workflows/sandpaper-version.txt b/.github/workflows/sandpaper-version.txt index ce62dc55b..ea98690dc 100644 --- a/.github/workflows/sandpaper-version.txt +++ b/.github/workflows/sandpaper-version.txt @@ -1 +1 @@ -0.16.9 +0.16.12 From 74e2ec20a4a55b07113d750debace872da3e4a60 Mon Sep 17 00:00:00 2001 From: VeronikaShevc <147643787+VeronikaShevc@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:19:07 +0000 Subject: [PATCH 25/33] Fix the typo in Markdown Code/Rendered Output table --- episodes/01-run-quit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/episodes/01-run-quit.md b/episodes/01-run-quit.md index 54763a8d0..258fb716c 100644 --- a/episodes/01-run-quit.md +++ b/episodes/01-run-quit.md @@ -355,7 +355,7 @@ Table: Showing some markdown syntax and its rendered output. | ``` |

| | 1. Use numbers | 1. Use numbers | | 1. to create | 2. to create | -| 1. bullet lists. | 3. numbered lists. | +| 1. numbered lists. | 3. numbered lists. | | ``` | | +---------------------------------------+------------------------------------------------+ +---------------------------------------+------------------------------------------------+ From 27630f1496eaee006fffe237db564758aa53554e Mon Sep 17 00:00:00 2001 From: Jost Migenda Date: Mon, 17 Nov 2025 17:27:15 +0000 Subject: [PATCH 26/33] Fix example output in "writing functions" episode Ensure output of example function matches the function definition above. --- episodes/16-writing-functions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/episodes/16-writing-functions.md b/episodes/16-writing-functions.md index a2f599d0f..c3d5f4280 100644 --- a/episodes/16-writing-functions.md +++ b/episodes/16-writing-functions.md @@ -58,6 +58,8 @@ print_greeting() ```output Hello! +The weather is nice today. +Right? ``` ## Arguments in a function call are matched to its defined parameters. From c3f3f1c07aa70ee15ca9b3d5eeafb4fcdd4f9cae Mon Sep 17 00:00:00 2001 From: zkamvar <3639446+zkamvar@users.noreply.github.com> Date: Tue, 27 Jan 2026 00:04:19 +0000 Subject: [PATCH 27/33] [actions] update sandpaper workflow to version 0.18.4 --- .github/workflows/README.md | 246 ++++++++++++----- .github/workflows/docker_apply_cache.yaml | 227 ++++++++++++++++ .github/workflows/docker_build_deploy.yaml | 156 +++++++++++ .github/workflows/docker_pr_receive.yaml | 302 +++++++++++++++++++++ .github/workflows/pr-comment.yaml | 116 +++++--- .github/workflows/pr-preflight.yaml | 2 +- .github/workflows/sandpaper-version.txt | 2 +- .github/workflows/update-cache.yaml | 155 ++++++++--- .github/workflows/update-workflows.yaml | 99 +++++-- 9 files changed, 1138 insertions(+), 167 deletions(-) create mode 100644 .github/workflows/docker_apply_cache.yaml create mode 100644 .github/workflows/docker_build_deploy.yaml create mode 100644 .github/workflows/docker_pr_receive.yaml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 18ab76509..a57a31c09 100755 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -1,68 +1,161 @@ # Carpentries Workflows -This directory contains workflows to be used for Lessons using the {sandpaper} -lesson infrastructure. Two of these workflows require R (`sandpaper-main.yaml` -and `pr-receive.yaml`) and the rest are bots to handle pull request management. +This directory contains workflows to be used for Lessons using the Carpentries Workbench lesson infrastructure. -These workflows will likely change as {sandpaper} evolves, so it is important to -keep them up-to-date. To do this in your lesson you can do the following in your -R console: +The three `docker-` workflows build lessons and maintain packages. +The workflows run using the [workbench-docker](https://github.com/carpentries/workbench-docker) container. +This container comprises prebuilt and installed dependencies of the core Workbench packages, i.e. sandpaper, pegboard and varnish. + +Two `update-` workflows handle: + - checking for new renv packages and creating a Pull Request (PR) when a renv.lock file updates (`update-cache.yaml`) + - checking for updated versions of these workflow files (`update-workflows.yaml`) + +The rest of the `pr-` workflows handle pull request management via base GitHub Actions. + +For Carpentries Core Curriculum lessons across our lesson programmes, maintenance of these workflows should be minimal. +For your own lesson repositories, it is important to understand the different workflows and what they do. + +## Managing Updates + +By using prebuilt Docker containers that are managed by the Carpentries core Workbench maintainers, these workflows are designed to be rarely updated. + +However, is important to be able to keep them up-to-date when appropriate. +You can do this locally using your own R and Workbench installation, or via the "04 Maintain: Update Workflow Files" (`update-workflows.yaml`) GitHub Action. + +### Updating locally + +In a terminal/git bash, navigate to the lesson folder where you want to update the workflows. + +Then, start an R session and: ```r # Install/Update sandpaper -options(repos = c(carpentries = "https://carpentries.r-universe.dev/", - CRAN = "https://cloud.r-project.org")) +options(repos = c(carpentries = "https://carpentries.r-universe.dev/", CRAN = "https://cloud.r-project.org")) install.packages("sandpaper") # update the workflows in your lesson library("sandpaper") -update_github_workflows() +sandpaper::update_github_workflows() +quit() ``` -Inside this folder, you will find a file called `sandpaper-version.txt`, which -will contain a version number for sandpaper. This will be used in the future to -alert you if a workflow update is needed. +And then in a bash prompt/git bash terminal: -What follows are the descriptions of the workflow files: +```bash +$ git add .github/workflows +$ git commit -m "Manual update to docker workflows" +$ git push origin main +``` -## Deployment +This will automatically start the "01 Maintain: Build and Deploy Site" workflow. -### 01 Build and Deploy (sandpaper-main.yaml) +This will be the extent of requirements for non-renv lessons. -This is the main driver that will only act on the main branch of the repository. -This workflow does the following: +#### Lessons that use Rmd and {renv} + +For renv-enabled lessons: +- Cancel the "01 Maintain: Build and Deploy Site" run that automatically started following the push to main +- Run the "02 Maintain: Check for Updated Packages" +- Run the "03 Maintain: Apply Package Cache" +- Run the "01 Maintain: Build and Deploy Site" + +### Updating using GitHub + +This presumes you: + - already have a lesson repository available on GitHub + - have enabled workflows in the lesson repo + - have set up a SANDPAPER_WORKFLOW personal access token (PAT) in the lesson repo + +To go through these steps, please follow the [Forking a Workbench Lesson](https://docs.carpentries.org/resources/curriculum/lesson-forks.html#forking-a-workbench-lesson-repository) +documentation. + +Once set up, run the "04 Maintain: Update Workflow Files" (`update-workflows.yaml`) action. + +This will raise a PR with any changes to the workflows that are needed. +If you are happy with the changes made, you can merge the PR into your lesson repository. +## Lesson Builds and Deployment + +### 01 Maintain: Build and Deploy Site (docker_build_deploy.yaml) + +This is the main workflow that you will encounter most often. + +It will only act on the main branch of the lesson repository. + +This workflow does the following: 1. checks out the lesson 2. provisions the following resources - - R - - pandoc - - lesson infrastructure (stored in a cache) - - lesson dependencies if needed (stored in a cache) + - the Workbench Docker container + - lesson dependencies if needed (stored in a cache) 3. builds the lesson via `sandpaper:::ci_deploy()` +If your lesson contains rendered content using RMarkdown and/or any associated R package dependencies, you will need to generate and apply the renv cache. +Please read the [Caching](#caching) section below. + #### Caching -This workflow has two caches; one cache is for the lesson infrastructure and -the other is for the lesson dependencies if the lesson contains rendered -content. These caches are invalidated by new versions of the infrastructure and -the `renv.lock` file, respectively. If there is a problem with the cache, -manual invaliation is necessary. You will need maintain access to the repository -and you can either go to the actions tab and [click on the caches button to find -and invalidate the failing cache](https://github.blog/changelog/2022-10-20-manage-caches-in-your-actions-workflows-from-web-interface/) -or by setting the `CACHE_VERSION` secret to the current date (which will -invalidate all of the caches). +> [!NOTE] +> Caching is only relevant for lessons that use Rmd files and renv to manage R packages. +> If you are building basic markdown documents, caching will not apply to you, and the only +> workflow that needs to be run is "01 Maintain: Build and Deploy Site". + +In summary, generating a reusable package cache is achieved by running the "02 Maintain: Check for Updated Packages" workflow, and then the "03 Maintain: Apply Package Cache" workflow. + +These workflows are separated to ensure that once you have a successful build with a working renv cache, this cache is stored within GitHub's infrastructure, and will be reused by the Workbench Docker container. +This means that lesson builds will be faster once an renv cache is created and reused by the Docker container. + +Another major bonus of this setup is that you can keep using this cache indefinitely to build your lesson. +This is important if you need very specific versions of R packages ("pinning"). + +If and when you want to perform an update to the cache, you can re-run the "02 Maintain: Check for Updated Packages" and verify that your lesson still builds with the new packages. +If all looks good, re-run the "03 Maintain: Apply Package Cache" workflow, and this will write a new renv cache file to GitHub. + +In any case, the renv cache is invalidated by new versions of the `renv.lock` file. +This happens: + - if you update your lockfile locally by using the `sandpaper::update_cache()` function, and then push it to the lesson repository + - when you run the "02 Maintain: Check for Updated Packages" and there are new packages to install + +More information on managing local renv caches for lessons can be found in the [Sandpaper packages vignettes](https://carpentries.github.io/sandpaper/articles/building-with-renv.html). + +#### Using different package cache versions + +There are times when you may want to go back to a previous renv package cache file: + - if you run "02 Maintain: Check for Updated Packages" and "03 Maintain: Apply Package Cache" and the cache generation fails for some reason + - if there is a new R package that produces incorrect or broken lesson output + +To choose a previous cache file version for your builds, go to the Actions tab, and click Caches in the left hand pane. + +Cache files should have the following name format: + +``` + OS HASHSUM +[ | ] [ | ] +Linux--renv-2e499eb706112971b2cffceb49b55a6efe49f3ed75cd6579b10ff224489daca4 +``` + +Once you have 2 or more cache files, you can choose which one you want to use. + +Copy the hashsum part of the desired cache file you want to use, e.g. `2e499eb706112971b2cffceb49b55a6efe49f3ed75cd6579b10ff224489daca4`. + +Then either: + 1. Add a repository variable called CACHE_VERSION, and paste in the hash + - Go to ... + 2. Run the "01 Maintain: Build and Deploy Site" manually, supplying the CACHE_VERSION input + - Go to ... + +If you have no caches listed, make sure to run the "02 Maintain: Check for Updated Packages" and "03 Maintain: Apply Package Cache" to create a new renv cache file. ## Updates ### Setup Information -These workflows run on a schedule and at the maintainer's request. Because they -create pull requests that update workflows/require the downstream actions to run, +These workflows run on a mix of schedules, automatic triggers, and at the maintainer's request. +Because they create pull requests that update workflows/require the downstream actions to run, they need a special repository/organization secret token called `SANDPAPER_WORKFLOW` and it must have the `public_repo` and `workflow` scope. This can be an individual user token, OR it can be a trusted bot account. If you -have a repository in one of the official Carpentries accounts, then you do not +have a repository in one of the official Carpentries organisations, then you do not need to worry about this token being present because the Carpentries Core Team will take care of supplying this token. @@ -73,45 +166,69 @@ clipboard and then go to your repository's settings > secrets > actions and create or edit the `SANDPAPER_WORKFLOW` secret, pasting in the generated token. If you do not specify your token correctly, the runs will not fail and they will -give you instructions to provide the token for your repository. +give you instructions to provide the token for your repository. + +### "02 Maintain: Check for Updated Packages" (update-cache.yaml) + +For lessons that have generated content, we use {renv} to ensure that the output +is stable. This is controlled by a single lockfile which documents the packages +needed for the lesson and the version numbers. This workflow is skipped in +lessons that do not have generated content. + +Packages are frequently updated, fixing bugs or introducing new features. It's a +good idea to make sure these packages can be both: updated periodically, or; or left +static to ensure consistent lesson builds. + +The update cache workflow will do this by: +- checking repositories for updates +- updating the renv lockfile +- summarising the updated packages and their versions in a branch called `updates/packages` +- creating a pull request with _only the renv lockfile changed_ + +From here, the markdown documents will be rebuilt and you can inspect what has +changed based on how the packages have updated. -### 02 Maintain: Update Workflow Files (update-workflow.yaml) +If all steps pass in this workflow, you can safely merge the PR that is raised. +Once the PR is merged, the "03 Maintain: Apply Package Cache" workflow will run +automatically. + +### 03 Maintain: Apply Package Cache (docker_apply_cache.yaml) + +This workflow takes the updated lockfile produced in "02 Maintain: Check for Updated Packages" +and uses it to produce a cached file stored within GitHub's infrastructure. + +This cached file can then be reused repeatedly by the "01 Maintain: Build and Deploy Site" +workflow. + +This workflow is run automatically when the PR generated by "02 Maintain: Check for Updated Packages" +is closed and merged. + +You would only ever need to run this workflow manually: +- if your cache gets removed by GitHub due to age or non-use +- if your cache file contains packages that cannot be used by a Workbench Docker container's newer R version + +### "04 Maintain: Update Workflow Files" (update-workflows.yaml) The {sandpaper} repository was designed to do as much as possible to separate -the tools from the content. For local builds, this is absolutely true, but -there is a minor issue when it comes to workflow files: they must live inside -the repository. +the tools from the content. For local builds, this is absolutely true as you +can develop and build lessons without any GitHub workflows. When it comes to +workflow files on GitHub itself for managed builds online, the workflows must +live inside the lesson repository. -This workflow ensures that the workflow files are up-to-date. The way it work is -to download the update-workflows.sh script from GitHub and run it. The script -will do the following: +This workflow ensures that the workflow files are up-to-date. It downloads the +`update-workflows.sh` script from GitHub and runs it. The script will do the +following: -1. check the recorded version of sandpaper against the current version on github +1. check the recorded version of sandpaper against the current version on GitHub 2. update the files if there is a difference in versions -After the files are updated, if there are any changes, they are pushed to a +After the files are updated, and if there are any changes, they are pushed to a branch called `update/workflows` and a pull request is created. Maintainers are encouraged to review the changes and accept the pull request if the outputs are okay. This update is run weekly or on demand. -### 03 Maintain: Update Package Cache (update-cache.yaml) - -For lessons that have generated content, we use {renv} to ensure that the output -is stable. This is controlled by a single lockfile which documents the packages -needed for the lesson and the version numbers. This workflow is skipped in -lessons that do not have generated content. - -Because the lessons need to remain current with the package ecosystem, it's a -good idea to make sure these packages can be updated periodically. The -update cache workflow will do this by checking for updates, applying them in a -branch called `updates/packages` and creating a pull request with _only the -lockfile changed_. - -From here, the markdown documents will be rebuilt and you can inspect what has -changed based on how the packages have updated. - ## Pull Request and Review Management Because our lessons execute code, pull requests are a secruity risk for any @@ -140,11 +257,11 @@ Once the checks are finished, a comment is issued to the pull request, which will allow maintainers to determine if it is safe to run the "Receive Pull Request" workflow from new contributors. -### Receive Pull Request (pr-receive.yaml) +### Receive Pull Request (docker_pr_receive.yaml) **Note of caution:** This workflow runs arbitrary code by anyone who creates a pull request. GitHub has safeguarded the token used in this workflow to have no -priviledges in the repository, but we have taken precautions to protect against +privileges in the repository, but we have taken precautions to protect against spoofing. This workflow is triggered with every push to a pull request. If this workflow @@ -164,14 +281,11 @@ request. This builds the content and uploads three artifacts: 2. A summary of changes after the rendering process (diff) 3. The rendered files (build) -Because this workflow builds generated content, it follows the same general -process as the `sandpaper-main` workflow with the same caching mechanisms. - -The artifacts produced are used by the next workflow. +The artifacts produced are used by the "Comment on Pull Request" workflow. ### Comment on Pull Request (pr-comment.yaml) -This workflow is triggered if the `pr-receive.yaml` workflow is successful. +This workflow is triggered if the `docker_pr_receive.yaml` workflow is successful. The steps in this workflow are: 1. Test if the workflow is valid and comment the validity of the workflow to the diff --git a/.github/workflows/docker_apply_cache.yaml b/.github/workflows/docker_apply_cache.yaml new file mode 100644 index 000000000..0cb66370d --- /dev/null +++ b/.github/workflows/docker_apply_cache.yaml @@ -0,0 +1,227 @@ +name: "03 Maintain: Apply Package Cache" +description: "Generate the package cache for the lesson after a pull request has been merged or via manual trigger, and cache in S3 or GitHub" +on: + workflow_dispatch: + inputs: + name: + description: 'Who triggered this build?' + required: true + default: 'Maintainer (via GitHub)' + pull_request: + types: + - closed + branches: + - main + +# queue cache runs +concurrency: + group: docker-apply-cache + cancel-in-progress: false + +jobs: + preflight: + name: "Preflight: PR or Manual Trigger?" + runs-on: ubuntu-latest + outputs: + do-apply: ${{ steps.check.outputs.merged_or_manual }} + steps: + - name: "Should we run cache application?" + id: check + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" || + ("${{ github.ref }}" == "refs/heads/main" && "${{ github.event.action }}" == "closed" && "${{ github.event.pull_request.merged }}" == "true") ]]; then + echo "merged_or_manual=true" >> $GITHUB_OUTPUT + else + echo "This was not a manual trigger and no PR was merged. No action taken." + echo "merged_or_manual=false" >> $GITHUB_OUTPUT + fi + shell: bash + + check-renv: + name: "Check If We Need {renv}" + runs-on: ubuntu-latest + needs: preflight + if: needs.preflight.outputs.do-apply == 'true' + permissions: + id-token: write + outputs: + renv-needed: ${{ steps.check-for-renv.outputs.renv-needed }} + renv-cache-hashsum: ${{ steps.check-for-renv.outputs.renv-cache-hashsum }} + renv-cache-available: ${{ steps.check-for-renv.outputs.renv-cache-available }} + steps: + - name: "Check for renv" + id: check-for-renv + uses: carpentries/actions/renv-checks@main + with: + role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }} + aws-region: ${{ secrets.AWS_GH_OIDC_REGION }} + WORKBENCH_TAG: ${{ vars.WORKBENCH_TAG || 'latest' }} + token: ${{ secrets.GITHUB_TOKEN }} + + no-renv-cache-used: + name: "No renv cache used" + runs-on: ubuntu-latest + needs: check-renv + if: needs.check-renv.outputs.renv-needed != 'true' + steps: + - name: "No renv cache needed" + run: echo "No renv cache needed for this lesson" + + renv-cache-available: + name: "renv cache available" + runs-on: ubuntu-latest + needs: check-renv + if: needs.check-renv.outputs.renv-cache-available == 'true' + steps: + - name: "renv cache available" + run: echo "renv cache available for this lesson" + + update-renv-cache: + name: "Update renv Cache" + runs-on: ubuntu-latest + needs: check-renv + if: | + needs.check-renv.outputs.renv-needed == 'true' && + needs.check-renv.outputs.renv-cache-available != 'true' && + ( + github.event_name == 'workflow_dispatch' || + ( + github.event.pull_request.merged == true && + ( + ( + contains( + join(github.event.pull_request.labels.*.name, ','), + 'type: package cache' + ) && + github.event.pull_request.head.ref == 'update/packages' + ) + || + ( + contains( + join(github.event.pull_request.labels.*.name, ','), + 'type: workflows' + ) && + github.event.pull_request.head.ref == 'update/workflows' + ) + || + ( + contains( + join(github.event.pull_request.labels.*.name, ','), + 'type: docker version' + ) && + github.event.pull_request.head.ref == 'update/workbench-docker-version' + ) + ) + ) + ) + permissions: + checks: write + contents: write + pages: write + id-token: write + container: + image: ghcr.io/carpentries/workbench-docker:${{ vars.WORKBENCH_TAG || 'latest' }} + env: + WORKBENCH_PROFILE: "ci" + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + RENV_PATHS_ROOT: /home/rstudio/lesson/renv + RENV_PROFILE: "lesson-requirements" + RENV_VERSION: ${{ needs.check-renv.outputs.renv-cache-hashsum }} + RENV_CONFIG_EXTERNAL_LIBRARIES: "/usr/local/lib/R/site-library" + volumes: + - ${{ github.workspace }}:/home/rstudio/lesson + options: --cpus 2 + steps: + - uses: actions/checkout@v4 + + - name: "Debugging Info" + run: | + echo "Current Directory: $(pwd)" + ls -lah /home/rstudio/.workbench + ls -lah $(pwd) + Rscript -e 'sessionInfo()' + shell: bash + + - name: "Mark Repository as Safe" + run: | + git config --global --add safe.directory $(pwd) + shell: bash + + - name: "Ensure sandpaper is loadable" + run: | + .libPaths() + library(sandpaper) + shell: Rscript {0} + + - name: "Setup Lesson Dependencies" + run: | + Rscript /home/rstudio/.workbench/setup_lesson_deps.R + shell: bash + + - name: "Fortify renv Cache" + run: | + Rscript /home/rstudio/.workbench/fortify_renv_cache.R + shell: bash + + - name: "Get Container Version Used" + id: wb-vers + uses: carpentries/actions/container-version@main + with: + WORKBENCH_TAG: ${{ vars.WORKBENCH_TAG }} + renv-needed: ${{ needs.check-renv.outputs.renv-needed }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: "Validate Current Org and Workflow" + id: validate-org-workflow + uses: carpentries/actions/validate-org-workflow@main + with: + repo: ${{ github.repository }} + workflow: ${{ github.workflow }} + + - name: "Configure AWS credentials via OIDC" + id: aws-creds + env: + role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }} + aws-region: ${{ secrets.AWS_GH_OIDC_REGION }} + if: | + steps.validate-org-workflow.outputs.is_valid == 'true' && + env.role-to-assume != '' && + env.aws-region != '' + uses: aws-actions/configure-aws-credentials@v5.0.0 + with: + role-to-assume: ${{ env.role-to-assume }} + aws-region: ${{ env.aws-region }} + output-credentials: true + + - name: "Upload cache object to S3" + id: upload-cache + uses: carpentries/actions-cache@frog-matchedkey-1 + with: + accessKey: ${{ steps.aws-creds.outputs.aws-access-key-id }} + secretKey: ${{ steps.aws-creds.outputs.aws-secret-access-key }} + sessionToken: ${{ steps.aws-creds.outputs.aws-session-token }} + bucket: workbench-docker-caches + path: | + /home/rstudio/lesson/renv + /usr/local/lib/R/site-library + key: ${{ github.repository }}/${{ steps.wb-vers.outputs.container-version }}_renv-${{ needs.check-renv.outputs.renv-cache-hashsum }} + restore-keys: + ${{ github.repository }}/${{ steps.wb-vers.outputs.container-version }}_renv- + + trigger-build-deploy: + name: "Trigger Build and Deploy Workflow" + runs-on: ubuntu-latest + needs: update-renv-cache + if: | + needs.update-renv-cache.result == 'success' || + needs.check-renv.outputs.renv-cache-available == 'true' + steps: + - uses: actions/checkout@v4 + + - name: "Trigger Build and Deploy Workflow" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh workflow run docker_build_deploy.yaml --ref main + shell: bash + continue-on-error: true diff --git a/.github/workflows/docker_build_deploy.yaml b/.github/workflows/docker_build_deploy.yaml new file mode 100644 index 000000000..273439552 --- /dev/null +++ b/.github/workflows/docker_build_deploy.yaml @@ -0,0 +1,156 @@ +name: "01 Maintain: Build and Deploy Site" +description: "Build and deploy the lesson site using the carpentries/workbench-docker container" +on: + push: + branches: + - 'main' + paths-ignore: + - '.github/workflows/**.yaml' + - '.github/workbench-docker-version.txt' + schedule: + - cron: '0 0 * * 2' + workflow_dispatch: + inputs: + name: + description: 'Who triggered this build?' + required: true + default: 'Maintainer (via GitHub)' + CACHE_VERSION: + description: 'Optional renv cache version override' + required: false + default: '' + reset: + description: 'Reset cached markdown files' + required: true + default: false + type: boolean + force-skip-manage-deps: + description: 'Skip build-time dependency management' + required: true + default: false + type: boolean + +# only one build/deploy at a time +concurrency: + group: docker-build-deploy + cancel-in-progress: true + +jobs: + preflight: + name: "Preflight: Schedule, Push, or PR?" + runs-on: ubuntu-latest + outputs: + do-build: ${{ steps.build-check.outputs.do-build }} + renv-needed: ${{ steps.build-check.outputs.renv-needed }} + renv-cache-hashsum: ${{ steps.build-check.outputs.renv-cache-hashsum }} + workbench-container-file-exists: ${{ steps.wb-vers.outputs.workbench-container-file-exists }} + wb-vers: ${{ steps.wb-vers.outputs.container-version }} + last-wb-vers: ${{ steps.wb-vers.outputs.last-container-version }} + workbench-update: ${{ steps.wb-vers.outputs.workbench-update }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: "Should we run build and deploy?" + id: build-check + uses: carpentries/actions/build-preflight@main + + - name: "Checkout Lesson" + if: steps.build-check.outputs.do-build == 'true' + uses: actions/checkout@v4 + + - name: "Get container version info" + id: wb-vers + if: steps.build-check.outputs.do-build == 'true' + uses: carpentries/actions/container-version@main + with: + WORKBENCH_TAG: ${{ vars.WORKBENCH_TAG }} + renv-needed: ${{ steps.build-check.outputs.renv-needed }} + token: ${{ secrets.GITHUB_TOKEN }} + + full-build: + name: "Build Full Site" + runs-on: ubuntu-latest + needs: preflight + if: | + always() && + needs.preflight.outputs.do-build == 'true' && + needs.preflight.outputs.workbench-update != 'true' + env: + RENV_EXISTS: ${{ needs.preflight.outputs.renv-needed }} + RENV_HASH: ${{ needs.preflight.outputs.renv-cache-hashsum }} + permissions: + checks: write + contents: write + pages: write + id-token: write + container: + image: ghcr.io/carpentries/workbench-docker:${{ vars.WORKBENCH_TAG || 'latest' }} + env: + WORKBENCH_PROFILE: "ci" + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + RENV_PATHS_ROOT: /home/rstudio/lesson/renv + RENV_PROFILE: "lesson-requirements" + RENV_CONFIG_EXTERNAL_LIBRARIES: "/usr/local/lib/R/site-library" + volumes: + - ${{ github.workspace }}:/home/rstudio/lesson + options: --cpus 1 + steps: + - uses: actions/checkout@v4 + + - name: "Debugging Info" + run: | + cd /home/rstudio/lesson + echo "Current Directory: $(pwd)" + echo "RENV_HASH is $RENV_HASH" + ls -lah /home/rstudio/.workbench + ls -lah $(pwd) + Rscript -e 'sessionInfo()' + shell: bash + + - name: "Mark Repository as Safe" + run: | + git config --global --add safe.directory $(pwd) + shell: bash + + - name: "Setup Lesson Dependencies" + id: build-container-deps + uses: carpentries/actions/build-container-deps@main + with: + CACHE_VERSION: ${{ vars.CACHE_VERSION || github.event.inputs.CACHE_VERSION || '' }} + WORKBENCH_TAG: ${{ vars.WORKBENCH_TAG || 'latest' }} + LESSON_PATH: ${{ vars.LESSON_PATH || '/home/rstudio/lesson' }} + role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }} + aws-region: ${{ secrets.AWS_GH_OIDC_REGION }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: "Run Container and Build Site" + id: build-and-deploy + uses: carpentries/actions/build-and-deploy@main + with: + reset: ${{ vars.BUILD_RESET || github.event.inputs.reset || 'false' }} + skip-manage-deps: ${{ github.event.inputs.force-skip-manage-deps == 'true' || steps.build-container-deps.outputs.renv-cache-available || steps.build-container-deps.outputs.backup-cache-used || 'false' }} + + update-container-version: + name: "Update container version used" + runs-on: ubuntu-latest + needs: [preflight] + permissions: + actions: write + contents: write + pull-requests: write + id-token: write + if: | + needs.preflight.outputs.do-build == 'true' && + ( + needs.preflight.outputs.workbench-container-file-exists == 'false' || + needs.preflight.outputs.workbench-update == 'true' + ) + steps: + - name: "Record container version used" + uses: carpentries/actions/record-container-version@main + with: + CONTAINER_VER: ${{ needs.preflight.outputs.wb-vers }} + AUTO_MERGE: ${{ vars.AUTO_MERGE_CONTAINER_VERSION_UPDATE || 'true' }} + token: ${{ secrets.GITHUB_TOKEN }} + role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }} + aws-region: ${{ secrets.AWS_GH_OIDC_REGION }} diff --git a/.github/workflows/docker_pr_receive.yaml b/.github/workflows/docker_pr_receive.yaml new file mode 100644 index 000000000..12b16bf76 --- /dev/null +++ b/.github/workflows/docker_pr_receive.yaml @@ -0,0 +1,302 @@ +name: "Bot: Receive Pull Request" +description: "Receive a pull request and build the markdown source files" +on: + pull_request: + types: + [opened, synchronize, reopened] + workflow_dispatch: + inputs: + pr_number: + type: number + required: true + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: write + +jobs: + + preflight: + name: "Preflight: md-outputs exists?" + runs-on: ubuntu-latest + outputs: + branch-exists: ${{ steps.check.outputs.exists }} + steps: + - name: "Checkout Lesson" + uses: actions/checkout@v4 + + - name: "Check if md-outputs branch exists" + id: check + run: | + # 💡 Checking for md-outputs branch # + if [[ -n $(git ls-remote --exit-code --heads origin md-outputs) ]]; then + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "exists=false" >> $GITHUB_OUTPUT + echo "::error::md-outputs branch required but does not exist." + echo "::error::Please merge any open package update PRs to trigger the '03 Maintain: Apply Package Cache' and '01: Maintain: Build and Deploy Site' workflows." + + echo "## ❌ ERROR: md-outputs branch required" >> $GITHUB_STEP_SUMMARY + echo "Please merge any open package update PRs to trigger the '03 Maintain: Apply Package Cache' and '01: Maintain: Build and Deploy Site' workflows." >> $GITHUB_STEP_SUMMARY + + exit 1 + fi + shell: bash + + test-pr: + name: "Record PR number" + if: ${{ github.event.action != 'closed' }} && ${{ needs.preflight.outputs.branch-exists == 'true' }} + runs-on: ubuntu-latest + needs: preflight + outputs: + is_valid: ${{ steps.check-pr.outputs.VALID }} + pr_number: ${{ env.NR }} + pr_branch: ${{ env.PR_BRANCH }} + steps: + - name: "Grab PR" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if [[ "${{ github.event_name }}" == "pull_request" ]] ; then + PR_NUMBER=${{ github.event.number }} + elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]] ; then + PR_NUMBER=${{ inputs.pr_number }} + fi + + echo $PR_NUMBER > ${{ github.workspace }}/NR + echo "NR=$PR_NUMBER" >> $GITHUB_ENV + echo "PR_BRANCH=$(gh -R ${{ github.repository }} pr view $PR_NUMBER --json headRefName --jq '.headRefName')" >> $GITHUB_ENV + shell: bash + + - name: "Upload PR number" + id: upload + if: always() + uses: actions/upload-artifact@v4 + with: + name: pr + path: ${{ github.workspace }}/NR + + - name: "Get Invalid Hashes File" + id: hash + run: | + echo "json<> $GITHUB_OUTPUT + shell: bash + + - name: "Debug Hashes Output" + run: | + echo "${{ steps.hash.outputs.json }}" + shell: bash + + - name: "Check PR" + id: check-pr + uses: carpentries/actions/check-valid-pr@main + with: + pr: ${{ env.NR }} + invalid: ${{ fromJSON(steps.hash.outputs.json)[github.repository] }} + + check-renv: + name: "Check If We Need {renv}" + runs-on: ubuntu-latest + outputs: + renv-needed: ${{ steps.renv-check.outputs.renv-needed }} + renv-cache-hashsum: ${{ steps.renv-check.outputs.renv-cache-hashsum }} + steps: + - name: "Checkout Lesson" + uses: actions/checkout@v4 + + - name: "Is renv required?" + id: renv-check + uses: carpentries/actions/renv-checks@main + with: + CACHE_VERSION: ${{ inputs.CACHE_VERSION || '' }} + skip-cache-check: true + + build-md-source: + name: "Build markdown source files if valid" + needs: + - test-pr + - check-renv + runs-on: ubuntu-latest + if: needs.test-pr.outputs.is_valid == 'true' + env: + CHIVE: ${{ github.workspace }}/site/chive + PR: ${{ github.workspace }}/site/pr + GHWMD: ${{ github.workspace }}/site/built + PR_BRANCH: ${{ needs.test-pr.outputs.pr_branch }} + PR_NUMBER: ${{ needs.test-pr.outputs.pr_number }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + permissions: + checks: write + contents: write + pages: write + container: + image: ghcr.io/carpentries/workbench-docker:${{ vars.WORKBENCH_TAG || 'latest' }} + env: + WORKBENCH_PROFILE: "ci" + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RENV_PATHS_ROOT: /home/rstudio/lesson/renv + RENV_PROFILE: "lesson-requirements" + RENV_CONFIG_EXTERNAL_LIBRARIES: "/usr/local/lib/R/site-library" + volumes: + - ${{ github.workspace }}:/home/rstudio/lesson + options: --cpus 2 + outputs: + workbench-update: ${{ steps.wb-vers.outputs.workbench-update }} + build-site: ${{ steps.build-site.outcome }} + steps: + - uses: actions/checkout@v4 + + - name: "Check Out Staging Branch" + uses: actions/checkout@v4 + with: + ref: md-outputs + path: ${{ env.GHWMD }} + + - name: Mark Repository as Safe + run: | + git config --global --add safe.directory $(pwd) + git config --global --add safe.directory /home/rstudio/lesson + shell: bash + + - name: "Ensure sandpaper is loadable" + run: | + .libPaths() + library(sandpaper) + shell: Rscript {0} + + - name: Setup Lesson Dependencies + run: | + Rscript /home/rstudio/.workbench/setup_lesson_deps.R + shell: bash + + - name: Get Container Version Used + id: wb-vers + if: needs.check-renv.outputs.renv-needed == 'true' + uses: carpentries/actions/container-version@main + with: + WORKBENCH_TAG: ${{ vars.WORKBENCH_TAG }} + renv-needed: ${{ needs.check-renv.outputs.renv-needed }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: "Validate Current Org and Workflow" + id: validate-org-workflow + if: needs.check-renv.outputs.renv-needed == 'true' + uses: carpentries/actions/validate-org-workflow@main + with: + repo: ${{ github.repository }} + workflow: ${{ github.workflow }} + + - name: Configure AWS credentials via OIDC + id: aws-creds + env: + role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }} + aws-region: ${{ secrets.AWS_GH_OIDC_REGION }} + if: | + steps.validate-org-workflow.outputs.is_valid == 'true' && + needs.check-renv.outputs.renv-needed == 'true' && + env.role-to-assume != '' && + env.aws-region != '' + uses: aws-actions/configure-aws-credentials@v5.0.0 + with: + role-to-assume: ${{ env.role-to-assume }} + aws-region: ${{ env.aws-region }} + output-credentials: true + + - name: Get cache object from S3 + id: s3-cache + uses: carpentries/actions-cache/restore@frog-matchedkey-1 + if: needs.check-renv.outputs.renv-needed == 'true' + with: + # insecure: false # optional, use http instead of https. default false + accessKey: ${{ steps.aws-creds.outputs.aws-access-key-id }} + secretKey: ${{ steps.aws-creds.outputs.aws-secret-access-key }} + sessionToken: ${{ steps.aws-creds.outputs.aws-session-token }} + bucket: workbench-docker-caches + path: | + /home/rstudio/lesson/renv + /usr/local/lib/R/site-library + key: ${{ github.repository }}/${{ steps.wb-vers.outputs.container-version }}_renv-${{ needs.check-renv.outputs.renv-cache-hashsum }} + restore-keys: + ${{ github.repository }}/${{ steps.wb-vers.outputs.container-version }}_renv- + + - name: "Fortify renv Cache" + if: | + needs.check-renv.outputs.renv-needed == 'true' && + steps.s3-cache.outputs.cache-hit != 'true' + run: | + Rscript /home/rstudio/.workbench/fortify_renv_cache.R + shell: bash + + - name: "Validate and Build Markdown" + id: build-site + run: | + sandpaper::package_cache_trigger(TRUE) + sandpaper::validate_lesson(path = '/home/rstudio/lesson') + sandpaper:::build_markdown(path = '/home/rstudio/lesson', quiet = FALSE) + shell: Rscript {0} + + - name: "Generate Artifacts" + id: generate-artifacts + run: | + sandpaper:::ci_bundle_pr_artifacts( + repo = '${{ github.repository }}', + pr_number = '${{ env.PR_NUMBER }}', + path_md = '/home/rstudio/lesson/site/built', + path_pr = '/home/rstudio/lesson/site/pr', + path_archive = '/home/rstudio/lesson/site/chive', + branch = 'md-outputs' + ) + shell: Rscript {0} + + - name: "Upload PR" + uses: actions/upload-artifact@v4 + with: + name: pr + path: ${{ env.PR }} + overwrite: true + + - name: "Upload Diff" + uses: actions/upload-artifact@v4 + with: + name: diff + path: ${{ env.CHIVE }} + retention-days: 1 + + - name: "Upload Build" + uses: actions/upload-artifact@v4 + with: + name: built + path: ${{ env.GHWMD }} + retention-days: 1 + + - name: "Teardown" + run: sandpaper::reset_site() + shell: Rscript {0} + + pr-checks: + name: "Trigger PR Checks?" + needs: + - test-pr + - build-md-source + runs-on: ubuntu-latest + if: needs.test-pr.outputs.is_valid == 'true' + permissions: + actions: write + checks: write + steps: + - name: "Checkout Lesson" + uses: actions/checkout@v4 + + - name: "Trigger PR Checks" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh workflow run pr-comment.yaml --ref main --field workflow_id=${{ github.run_id }} + shell: bash diff --git a/.github/workflows/pr-comment.yaml b/.github/workflows/pr-comment.yaml index f80d9d0c0..cbf0e2b2c 100755 --- a/.github/workflows/pr-comment.yaml +++ b/.github/workflows/pr-comment.yaml @@ -1,40 +1,44 @@ name: "Bot: Comment on the Pull Request" - -# read-write repo token -# access to secrets +description: "Comment on the pull request with the results of the markdown generation" on: - workflow_run: - workflows: ["Receive Pull Request"] - types: - - completed + workflow_dispatch: + inputs: + workflow_id: + required: true concurrency: group: pr-${{ github.event.workflow_run.pull_requests[0].number }} cancel-in-progress: true - jobs: # Pull requests are valid if: # - they match the sha of the workflow run head commit # - they are open - # - no .github files were committed + # - no .github files were committed, except for .github/workbench-docker-version.txt test-pr: name: "Test if pull request is valid" - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest if: > - github.event.workflow_run.event == 'pull_request' && - github.event.workflow_run.conclusion == 'success' + github.event_name == 'workflow_dispatch' || + ( + github.event_name == 'workflow_run' && + ( + github.event.workflow_run.event == 'pull_request' || + github.event.workflow_run.event == 'workflow_dispatch' + ) && + github.event.workflow_run.conclusion == 'success' + ) outputs: is_valid: ${{ steps.check-pr.outputs.VALID }} payload: ${{ steps.check-pr.outputs.payload }} number: ${{ steps.get-pr.outputs.NUM }} msg: ${{ steps.check-pr.outputs.MSG }} steps: - - name: 'Download PR artifact' + - name: "Download PR artifact" id: dl uses: carpentries/actions/download-workflow-artifact@main with: - run: ${{ github.event.workflow_run.id }} + run: ${{ github.event.workflow_run.id || inputs.workflow_id }} name: 'pr' - name: "Get PR Number" @@ -50,12 +54,46 @@ jobs: run: | echo '::error::A pull request number was not recorded. The pull request that triggered this workflow is likely malicious.' exit 1 + + - name: "Checkout Lesson" + uses: actions/checkout@v4 + + - name: "Verify committed files" + id: changed-files + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + ## Get list of changed files in the PR ## + ONLY_VERSION=$(gh pr view ${{ steps.get-pr.outputs.NUM }} --json files --jq ' + .files | + length == 1 and + .[0].path == ".github/workbench-docker-version.txt" + ') + + if [[ "$ONLY_VERSION" == "true" ]]; then + echo "only_version_file=true" >> $GITHUB_OUTPUT + else + echo "only_version_file=false" >> $GITHUB_OUTPUT + fi + shell: bash + + - name: "Skip checks for Workbench version file updates" + if: steps.changed-files.outputs.only_version_file == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "Only workbench-docker-version.txt changed, skipping preflight checks and running cache update" + gh workflow run update-cache.yaml --ref main + exit 0 + shell: bash + - name: "Get Invalid Hashes File" id: hash run: | echo "json<> $GITHUB_OUTPUT + - name: "Check PR" id: check-pr if: ${{ steps.dl.outputs.success == 'true' }} @@ -67,6 +105,14 @@ jobs: invalid: ${{ fromJSON(steps.hash.outputs.json)[github.repository] }} fail_on_error: true + - name: "Comment result of validation" + id: comment-diff + if: always() + uses: carpentries/actions/comment-diff@main + with: + pr: ${{ steps.get-pr.outputs.NUM }} + body: ${{ steps.check-pr.outputs.MSG }} + # Create an orphan branch on this repository with two commits # - the current HEAD of the md-outputs branch # - the output from running the current HEAD of the pull request through @@ -74,32 +120,32 @@ jobs: create-branch: name: "Create Git Branch" needs: test-pr - runs-on: ubuntu-22.04 - if: ${{ needs.test-pr.outputs.is_valid == 'true' }} + runs-on: ubuntu-latest + if: needs.test-pr.outputs.is_valid == 'true' env: NR: ${{ needs.test-pr.outputs.number }} permissions: contents: write steps: - - name: 'Checkout md outputs' + - name: "Checkout md outputs" uses: actions/checkout@v4 with: ref: md-outputs path: built fetch-depth: 1 - - name: 'Download built markdown' + - name: "Download built markdown" id: dl uses: carpentries/actions/download-workflow-artifact@main with: - run: ${{ github.event.workflow_run.id }} + run: ${{ github.event.workflow_run.id || inputs.workflow_id }} name: 'built' - - if: ${{ steps.dl.outputs.success == 'true' }} + - if: steps.dl.outputs.success == 'true' run: unzip built.zip - name: "Create orphan and push" - if: ${{ steps.dl.outputs.success == 'true' }} + if: steps.dl.outputs.success == 'true' run: | cd built/ git config --local user.email "actions@github.com" @@ -120,26 +166,26 @@ jobs: comment-pr: name: "Comment on Pull Request" needs: [test-pr, create-branch] - runs-on: ubuntu-22.04 - if: ${{ needs.test-pr.outputs.is_valid == 'true' }} + runs-on: ubuntu-latest + if: needs.test-pr.outputs.is_valid == 'true' env: NR: ${{ needs.test-pr.outputs.number }} permissions: pull-requests: write steps: - - name: 'Download comment artifact' + - name: "Download comment artifact" id: dl uses: carpentries/actions/download-workflow-artifact@main with: - run: ${{ github.event.workflow_run.id }} + run: ${{ github.event.workflow_run.id || inputs.workflow_id }} name: 'diff' - - if: ${{ steps.dl.outputs.success == 'true' }} + - if: steps.dl.outputs.success == 'true' run: unzip ${{ github.workspace }}/diff.zip - name: "Comment on PR" id: comment-diff - if: ${{ steps.dl.outputs.success == 'true' }} + if: steps.dl.outputs.success == 'true' uses: carpentries/actions/comment-diff@main with: pr: ${{ env.NR }} @@ -150,24 +196,26 @@ jobs: comment-changed-workflow: name: "Comment if workflow files have changed" needs: test-pr - runs-on: ubuntu-22.04 - if: ${{ always() && needs.test-pr.outputs.is_valid == 'false' }} + runs-on: ubuntu-latest + if: | + always() && + needs.test-pr.outputs.is_valid == 'false' env: - NR: ${{ github.event.workflow_run.pull_requests[0].number }} + NR: ${{ needs.test-pr.outputs.number }} body: ${{ needs.test-pr.outputs.msg }} permissions: pull-requests: write steps: - - name: 'Check for spoofing' + - name: "Check for spoofing" id: dl uses: carpentries/actions/download-workflow-artifact@main with: - run: ${{ github.event.workflow_run.id }} + run: ${{ github.event.workflow_run.id || inputs.workflow_id }} name: 'built' - - name: 'Alert if spoofed' + - name: "Alert if spoofed" id: spoof - if: ${{ steps.dl.outputs.success == 'true' }} + if: steps.dl.outputs.success == 'true' run: | echo 'body<> $GITHUB_ENV echo '' >> $GITHUB_ENV diff --git a/.github/workflows/pr-preflight.yaml b/.github/workflows/pr-preflight.yaml index 34ad7aed0..d0d7420dc 100755 --- a/.github/workflows/pr-preflight.yaml +++ b/.github/workflows/pr-preflight.yaml @@ -11,7 +11,7 @@ jobs: test-pr: name: "Test if pull request is valid" if: ${{ github.event.action != 'closed' }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest outputs: is_valid: ${{ steps.check-pr.outputs.VALID }} permissions: diff --git a/.github/workflows/sandpaper-version.txt b/.github/workflows/sandpaper-version.txt index ea98690dc..0cc988469 100644 --- a/.github/workflows/sandpaper-version.txt +++ b/.github/workflows/sandpaper-version.txt @@ -1 +1 @@ -0.16.12 +0.18.4 diff --git a/.github/workflows/update-cache.yaml b/.github/workflows/update-cache.yaml index a011c0c06..27b6d1cd9 100755 --- a/.github/workflows/update-cache.yaml +++ b/.github/workflows/update-cache.yaml @@ -1,26 +1,45 @@ -name: "03 Maintain: Update Package Cache" - +name: "02 Maintain: Check for Updated Packages" +description: "Check for updated R packages and create a pull request to update the lesson's renv lockfile and package cache" on: + schedule: + - cron: '0 0 * * 2' workflow_dispatch: inputs: name: - description: 'Who triggered this build (enter github username to tag yourself)?' + description: 'Who triggered this build?' required: true - default: 'monthly run' - schedule: - # Run every tuesday - - cron: '0 0 * * 2' + default: 'Maintainer (via GitHub)' + force-renv-init: + description: 'Force full lockfile update?' + required: false + default: false + type: boolean + update-packages: + description: 'Install any package updates?' + required: false + default: true + type: boolean + generate-cache: + description: 'Generate separate package cache?' + required: false + default: false + type: boolean + +env: + LOCKFILE_CACHE_GEN: ${{ vars.LOCKFILE_CACHE_GEN || github.event.inputs.generate-cache || 'false' }} + FORCE_RENV_INIT: ${{ vars.FORCE_RENV_INIT || github.event.inputs.force-renv-init || 'false' }} + UPDATE_PACKAGES: ${{ vars.UPDATE_PACKAGES || github.event.inputs.update-packages || 'true' }} jobs: preflight: - name: "Preflight Check" - runs-on: ubuntu-22.04 + name: "Preflight: Manual or Scheduled Trigger?" + runs-on: ubuntu-latest outputs: ok: ${{ steps.check.outputs.ok }} steps: - id: check run: | - if [[ ${{ github.event_name }} == 'workflow_dispatch' ]]; then + if [[ "${{ github.event_name }}" == 'workflow_dispatch' ]]; then echo "ok=true" >> $GITHUB_OUTPUT echo "Running on request" # using single brackets here to avoid 08 being interpreted as octal @@ -33,48 +52,42 @@ jobs: echo "ok=false" >> $GITHUB_OUTPUT echo "Not Running Today" fi + shell: bash - check_renv: - name: "Check if We Need {renv}" - runs-on: ubuntu-22.04 + check-renv: + name: "Check If We Need {renv}" + runs-on: ubuntu-latest needs: preflight - if: ${{ needs.preflight.outputs.ok == 'true'}} + if: ${{ needs.preflight.outputs.ok == 'true' }} outputs: - needed: ${{ steps.renv.outputs.exists }} + renv-needed: ${{ steps.renv-check.outputs.renv-needed }} steps: - name: "Checkout Lesson" uses: actions/checkout@v4 - - id: renv - run: | - if [[ -d renv ]]; then - echo "exists=true" >> $GITHUB_OUTPUT - fi - check_token: - name: "Check SANDPAPER_WORKFLOW token" - runs-on: ubuntu-22.04 - needs: check_renv - if: ${{ needs.check_renv.outputs.needed == 'true' }} - outputs: - workflow: ${{ steps.validate.outputs.wf }} - repo: ${{ steps.validate.outputs.repo }} - steps: - - name: "validate token" - id: validate - uses: carpentries/actions/check-valid-credentials@main + - name: "Is renv required?" + id: renv-check + uses: carpentries/actions/renv-checks@main with: - token: ${{ secrets.SANDPAPER_WORKFLOW }} + CACHE_VERSION: ${{ inputs.CACHE_VERSION || '' }} + skip-cache-check: true update_cache: - name: "Update Package Cache" - needs: check_token - if: ${{ needs.check_token.outputs.repo== 'true' }} + name: "Create Package Update Pull Request" runs-on: ubuntu-22.04 + needs: check-renv + permissions: + contents: write + pull-requests: write + actions: write + issues: write + id-token: write + if: needs.check-renv.outputs.renv-needed == 'true' env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RENV_PATHS_ROOT: ~/.local/share/renv/ steps: - - name: "Checkout Lesson" uses: actions/checkout@v4 @@ -88,14 +101,60 @@ jobs: id: update uses: carpentries/actions/update-lockfile@main with: + update: ${{ env.UPDATE_PACKAGES }} + force-renv-init: ${{ env.FORCE_RENV_INIT }} + generate-cache: ${{ env.LOCKFILE_CACHE_GEN }} cache-version: ${{ secrets.CACHE_VERSION }} - - name: Create Pull Request + - name: "Validate Current Org and Workflow" + id: validate-org-workflow + uses: carpentries/actions/validate-org-workflow@main + with: + repo: ${{ github.repository }} + workflow: ${{ github.workflow }} + + - name: "Configure AWS credentials via OIDC" + env: + role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }} + aws-region: ${{ secrets.AWS_GH_OIDC_REGION }} + if: | + steps.validate-org-workflow.outputs.is_valid == 'true' && + env.role-to-assume != '' && + env.aws-region != '' + uses: aws-actions/configure-aws-credentials@v5.0.0 + with: + role-to-assume: ${{ env.role-to-assume }} + aws-region: ${{ env.aws-region }} + + - name: "Set PAT from AWS Secrets Manager" + env: + role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }} + aws-region: ${{ secrets.AWS_GH_OIDC_REGION }} + if: | + steps.validate-org-workflow.outputs.is_valid == 'true' && + env.role-to-assume != '' && + env.aws-region != '' + id: set-pat + run: | + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id carpentries-bot/github-pat \ + --query SecretString --output text) + PAT=$(echo "$SECRET" | jq -r .[]) + echo "::add-mask::$PAT" + echo "pat=$PAT" >> "$GITHUB_OUTPUT" + shell: bash + + # Create the PR with the following roles in order of preference: + # - Carpentries Bot classic PAT fetched from AWS (will only work in official Carpentries repos) + # - repo-scoped SANDPAPER_WORKFLOW classic PAT (will work in all scenarios) + # - default GITHUB_TOKEN (will work suitably, but workflows need to be triggered) + - name: "Create Pull Request" id: cpr - if: ${{ steps.update.outputs.n > 0 }} + if: | + steps.update.outputs.n > 0 uses: carpentries/create-pull-request@main with: - token: ${{ secrets.SANDPAPER_WORKFLOW }} + token: ${{ steps.set-pat.outputs.pat || secrets.SANDPAPER_WORKFLOW || secrets.GITHUB_TOKEN }} delete-branch: true branch: "update/packages" commit-message: "[actions] update ${{ steps.update.outputs.n }} packages" @@ -123,3 +182,19 @@ jobs: [1]: https://github.com/carpentries/create-pull-request/tree/main labels: "type: package cache" draft: false + + - name: "Skip PR creation" + if: steps.update.outputs.n == 0 + run: | + echo "No updates needed, skipping PR creation" + shell: bash + + # thanks @Bisaloo! - https://github.com/carpentries/sandpaper/issues/646#issuecomment-2829578435 + # only trigger checks manually if the validate-token step had no valid AWS or SANDPAPER_WORKFLOW token + - name: "Trigger checks" + if: | + steps.cpr.outputs.pull-request-number != '' && + steps.validate-org-workflow.outputs.is_valid != 'true' + run: | + gh workflow run docker_pr_receive.yaml --field pr_number=${{ steps.cpr.outputs.pull-request-number }} + shell: bash diff --git a/.github/workflows/update-workflows.yaml b/.github/workflows/update-workflows.yaml index 6414cf287..09ec1b638 100755 --- a/.github/workflows/update-workflows.yaml +++ b/.github/workflows/update-workflows.yaml @@ -1,55 +1,104 @@ -name: "02 Maintain: Update Workflow Files" - +name: "04 Maintain: Update Workflow Files" +description: "Update workflow files from the carpentries/sandpaper repository" on: + schedule: + - cron: '0 0 * * 2' workflow_dispatch: inputs: name: description: 'Who triggered this build (enter github username to tag yourself)?' required: true default: 'weekly run' + tarball: + description: 'Absolute URL to the desired sandpaper repo tarball' + required: false + default: '' clean: description: 'Workflow files/file extensions to clean (no wildcards, enter "" for none)' required: false default: '.yaml' - schedule: - # Run every Tuesday - - cron: '0 0 * * 2' jobs: - check_token: - name: "Check SANDPAPER_WORKFLOW token" - runs-on: ubuntu-22.04 - outputs: - workflow: ${{ steps.validate.outputs.wf }} - repo: ${{ steps.validate.outputs.repo }} - steps: - - name: "validate token" - id: validate - uses: carpentries/actions/check-valid-credentials@main - with: - token: ${{ secrets.SANDPAPER_WORKFLOW }} - update_workflow: name: "Update Workflow" - runs-on: ubuntu-22.04 - needs: check_token - if: ${{ needs.check_token.outputs.workflow == 'true' }} + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + id-token: write steps: - name: "Checkout Repository" uses: actions/checkout@v4 + - name: "Validate Current Org and Workflow" + id: validate-org-workflow + uses: carpentries/actions/validate-org-workflow@main + with: + repo: ${{ github.repository }} + workflow: ${{ github.workflow }} + + - name: Configure AWS credentials via OIDC + env: + role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }} + aws-region: ${{ secrets.AWS_GH_OIDC_REGION }} + if: | + steps.validate-org-workflow.outputs.is_valid == 'true' && + env.role-to-assume != '' && + env.aws-region != '' + uses: aws-actions/configure-aws-credentials@v5.0.0 + with: + role-to-assume: ${{ env.role-to-assume }} + aws-region: ${{ env.aws-region }} + + - name: Set PAT from AWS Secrets Manager + id: set-pat + env: + role-to-assume: ${{ secrets.AWS_GH_OIDC_ARN }} + aws-region: ${{ secrets.AWS_GH_OIDC_REGION }} + if: | + steps.validate-org-workflow.outputs.is_valid == 'true' && + env.role-to-assume != '' && + env.aws-region != '' + run: | + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id carpentries-bot/github-pat \ + --query SecretString --output text) + PAT=$(echo "$SECRET" | jq -r .[]) + echo "::add-mask::$PAT" + echo "pat=$PAT" >> "$GITHUB_OUTPUT" + shell: bash + + - name: "Validate token" + id: validate-token + uses: carpentries/actions/check-valid-credentials@main + with: + token: ${{ steps.set-pat.outputs.pat || secrets.SANDPAPER_WORKFLOW }} + + - name: "No Token Found: Skipping Workflow Update" + if: ${{ steps.validate-token.outputs.wf == 'false' }} + run: | + echo "❗No valid SANDPAPER_WORKFLOW token or PAT from AWS found, cannot update workflows." + + echo "## ❌ Workflow Update Failed" >> $GITHUB_STEP_SUMMARY + echo "No valid SANDPAPER_WORKFLOW token or PAT from AWS found, cannot update workflows." >> $GITHUB_STEP_SUMMARY + shell: bash + - name: Update Workflows id: update + if: ${{ steps.validate-token.outputs.wf == 'true' }} uses: carpentries/actions/update-workflows@main with: - clean: ${{ github.event.inputs.clean }} + repo: ${{ github.event.inputs.tarball || 'https://carpentries.r-universe.dev' }} + clean: ${{ github.event.inputs.clean || '.yaml' }} - name: Create Pull Request id: cpr - if: "${{ steps.update.outputs.new }}" + if: | + steps.update.outputs.new && + steps.validate-token.outputs.wf == 'true' uses: carpentries/create-pull-request@main with: - token: ${{ secrets.SANDPAPER_WORKFLOW }} + token: ${{ steps.set-pat.outputs.pat || secrets.SANDPAPER_WORKFLOW }} delete-branch: true branch: "update/workflows" commit-message: "[actions] update sandpaper workflow to version ${{ steps.update.outputs.new }}" @@ -62,5 +111,5 @@ jobs: - Auto-generated by [create-pull-request][1] on ${{ steps.update.outputs.date }} [1]: https://github.com/carpentries/create-pull-request/tree/main - labels: "type: template and tools" + labels: "type: workflows" draft: false From 01c38a627415c9e09819d5452e372b737d270fbb Mon Sep 17 00:00:00 2001 From: "The Carpentries Apprentice (beta)" <64428345+carpentries-bot@users.noreply.github.com> Date: Wed, 28 Jan 2026 03:44:56 +0000 Subject: [PATCH 28/33] [actions] update workbench docker version to v0.2.4 (#709) Co-authored-by: alee <22534+alee@users.noreply.github.com> --- .github/workbench-docker-version.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/workbench-docker-version.txt diff --git a/.github/workbench-docker-version.txt b/.github/workbench-docker-version.txt new file mode 100644 index 000000000..f82e0685d --- /dev/null +++ b/.github/workbench-docker-version.txt @@ -0,0 +1 @@ +v0.2.4 From 3a725969555bd610fc956fdc927b163b23212cef Mon Sep 17 00:00:00 2001 From: zkamvar <3639446+zkamvar@users.noreply.github.com> Date: Tue, 3 Feb 2026 00:06:14 +0000 Subject: [PATCH 29/33] [actions] update sandpaper workflow to version 0.18.5 --- .github/workflows/docker_apply_cache.yaml | 26 ++-- .github/workflows/docker_build_deploy.yaml | 5 +- .github/workflows/docker_pr_receive.yaml | 27 +--- .github/workflows/pr-close-signal.yaml | 0 .github/workflows/pr-comment.yaml | 36 ++--- .github/workflows/pr-post-remove-branch.yaml | 0 .github/workflows/pr-preflight.yaml | 0 .github/workflows/pr-receive.yaml | 132 ------------------- .github/workflows/sandpaper-main.yaml | 64 --------- .github/workflows/sandpaper-version.txt | 2 +- .github/workflows/update-cache.yaml | 12 +- .github/workflows/update-workflows.yaml | 0 12 files changed, 34 insertions(+), 270 deletions(-) mode change 100755 => 100644 .github/workflows/pr-close-signal.yaml mode change 100755 => 100644 .github/workflows/pr-comment.yaml mode change 100755 => 100644 .github/workflows/pr-post-remove-branch.yaml mode change 100755 => 100644 .github/workflows/pr-preflight.yaml delete mode 100755 .github/workflows/pr-receive.yaml delete mode 100755 .github/workflows/sandpaper-main.yaml mode change 100755 => 100644 .github/workflows/update-cache.yaml mode change 100755 => 100644 .github/workflows/update-workflows.yaml diff --git a/.github/workflows/docker_apply_cache.yaml b/.github/workflows/docker_apply_cache.yaml index 0cb66370d..2c3a3bce2 100644 --- a/.github/workflows/docker_apply_cache.yaml +++ b/.github/workflows/docker_apply_cache.yaml @@ -208,20 +208,22 @@ jobs: restore-keys: ${{ github.repository }}/${{ steps.wb-vers.outputs.container-version }}_renv- - trigger-build-deploy: - name: "Trigger Build and Deploy Workflow" + record-cache-result: + name: "Record Caching Status" runs-on: ubuntu-latest - needs: update-renv-cache - if: | - needs.update-renv-cache.result == 'success' || - needs.check-renv.outputs.renv-cache-available == 'true' + needs: [check-renv, update-renv-cache] + if: always() + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v4 + - name: "Record cache result" - - name: "Trigger Build and Deploy Workflow" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh workflow run docker_build_deploy.yaml --ref main + echo "${{ needs.update-renv-cache.result == 'success' || needs.check-renv.outputs.renv-cache-available == 'true' || 'false' }}" > ${{ github.workspace }}/apply-cache-result shell: bash - continue-on-error: true + + - name: "Upload cache result" + uses: actions/upload-artifact@v4 + with: + name: apply-cache-result + path: ${{ github.workspace }}/apply-cache-result diff --git a/.github/workflows/docker_build_deploy.yaml b/.github/workflows/docker_build_deploy.yaml index 273439552..df3e8d1b8 100644 --- a/.github/workflows/docker_build_deploy.yaml +++ b/.github/workflows/docker_build_deploy.yaml @@ -9,6 +9,10 @@ on: - '.github/workbench-docker-version.txt' schedule: - cron: '0 0 * * 2' + workflow_run: + workflows: ["03 Maintain: Apply Package Cache"] + types: + - completed workflow_dispatch: inputs: name: @@ -72,7 +76,6 @@ jobs: runs-on: ubuntu-latest needs: preflight if: | - always() && needs.preflight.outputs.do-build == 'true' && needs.preflight.outputs.workbench-update != 'true' env: diff --git a/.github/workflows/docker_pr_receive.yaml b/.github/workflows/docker_pr_receive.yaml index 12b16bf76..3d01d9dc5 100644 --- a/.github/workflows/docker_pr_receive.yaml +++ b/.github/workflows/docker_pr_receive.yaml @@ -19,7 +19,6 @@ permissions: pull-requests: write jobs: - preflight: name: "Preflight: md-outputs exists?" runs-on: ubuntu-latest @@ -49,7 +48,9 @@ jobs: test-pr: name: "Record PR number" - if: ${{ github.event.action != 'closed' }} && ${{ needs.preflight.outputs.branch-exists == 'true' }} + if: | + github.event.action != 'closed' && + needs.preflight.outputs.branch-exists == 'true' runs-on: ubuntu-latest needs: preflight outputs: @@ -135,6 +136,7 @@ jobs: checks: write contents: write pages: write + id-token: write container: image: ghcr.io/carpentries/workbench-docker:${{ vars.WORKBENCH_TAG || 'latest' }} env: @@ -279,24 +281,3 @@ jobs: - name: "Teardown" run: sandpaper::reset_site() shell: Rscript {0} - - pr-checks: - name: "Trigger PR Checks?" - needs: - - test-pr - - build-md-source - runs-on: ubuntu-latest - if: needs.test-pr.outputs.is_valid == 'true' - permissions: - actions: write - checks: write - steps: - - name: "Checkout Lesson" - uses: actions/checkout@v4 - - - name: "Trigger PR Checks" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh workflow run pr-comment.yaml --ref main --field workflow_id=${{ github.run_id }} - shell: bash diff --git a/.github/workflows/pr-close-signal.yaml b/.github/workflows/pr-close-signal.yaml old mode 100755 new mode 100644 diff --git a/.github/workflows/pr-comment.yaml b/.github/workflows/pr-comment.yaml old mode 100755 new mode 100644 index cbf0e2b2c..7614abd03 --- a/.github/workflows/pr-comment.yaml +++ b/.github/workflows/pr-comment.yaml @@ -1,14 +1,10 @@ name: "Bot: Comment on the Pull Request" description: "Comment on the pull request with the results of the markdown generation" on: - workflow_dispatch: - inputs: - workflow_id: - required: true - -concurrency: - group: pr-${{ github.event.workflow_run.pull_requests[0].number }} - cancel-in-progress: true + workflow_run: + workflows: ["Bot: Receive Pull Request"] + types: + - completed jobs: # Pull requests are valid if: @@ -18,16 +14,6 @@ jobs: test-pr: name: "Test if pull request is valid" runs-on: ubuntu-latest - if: > - github.event_name == 'workflow_dispatch' || - ( - github.event_name == 'workflow_run' && - ( - github.event.workflow_run.event == 'pull_request' || - github.event.workflow_run.event == 'workflow_dispatch' - ) && - github.event.workflow_run.conclusion == 'success' - ) outputs: is_valid: ${{ steps.check-pr.outputs.VALID }} payload: ${{ steps.check-pr.outputs.payload }} @@ -38,7 +24,7 @@ jobs: id: dl uses: carpentries/actions/download-workflow-artifact@main with: - run: ${{ github.event.workflow_run.id || inputs.workflow_id }} + run: ${{ github.event.workflow_run.id }} name: 'pr' - name: "Get PR Number" @@ -79,11 +65,9 @@ jobs: - name: "Skip checks for Workbench version file updates" if: steps.changed-files.outputs.only_version_file == 'true' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - echo "Only workbench-docker-version.txt changed, skipping preflight checks and running cache update" - gh workflow run update-cache.yaml --ref main + echo "# 🔧 Wait for Next Cache Update #" + echo "Only workbench-docker-version.txt changed." exit 0 shell: bash @@ -138,7 +122,7 @@ jobs: id: dl uses: carpentries/actions/download-workflow-artifact@main with: - run: ${{ github.event.workflow_run.id || inputs.workflow_id }} + run: ${{ github.event.workflow_run.id }} name: 'built' - if: steps.dl.outputs.success == 'true' @@ -177,7 +161,7 @@ jobs: id: dl uses: carpentries/actions/download-workflow-artifact@main with: - run: ${{ github.event.workflow_run.id || inputs.workflow_id }} + run: ${{ github.event.workflow_run.id }} name: 'diff' - if: steps.dl.outputs.success == 'true' @@ -210,7 +194,7 @@ jobs: id: dl uses: carpentries/actions/download-workflow-artifact@main with: - run: ${{ github.event.workflow_run.id || inputs.workflow_id }} + run: ${{ github.event.workflow_run.id }} name: 'built' - name: "Alert if spoofed" diff --git a/.github/workflows/pr-post-remove-branch.yaml b/.github/workflows/pr-post-remove-branch.yaml old mode 100755 new mode 100644 diff --git a/.github/workflows/pr-preflight.yaml b/.github/workflows/pr-preflight.yaml old mode 100755 new mode 100644 diff --git a/.github/workflows/pr-receive.yaml b/.github/workflows/pr-receive.yaml deleted file mode 100755 index 7fbff6cdd..000000000 --- a/.github/workflows/pr-receive.yaml +++ /dev/null @@ -1,132 +0,0 @@ -name: "Receive Pull Request" - -on: - pull_request: - types: - [opened, synchronize, reopened] - -concurrency: - group: ${{ github.ref }} - cancel-in-progress: true - -jobs: - test-pr: - name: "Record PR number" - if: ${{ github.event.action != 'closed' }} - runs-on: ubuntu-22.04 - outputs: - is_valid: ${{ steps.check-pr.outputs.VALID }} - steps: - - name: "Record PR number" - id: record - if: ${{ always() }} - run: | - echo ${{ github.event.number }} > ${{ github.workspace }}/NR # 2022-03-02: artifact name fixed to be NR - - name: "Upload PR number" - id: upload - if: ${{ always() }} - uses: actions/upload-artifact@v4 - with: - name: pr - path: ${{ github.workspace }}/NR - - name: "Get Invalid Hashes File" - id: hash - run: | - echo "json<> $GITHUB_OUTPUT - - name: "echo output" - run: | - echo "${{ steps.hash.outputs.json }}" - - name: "Check PR" - id: check-pr - uses: carpentries/actions/check-valid-pr@main - with: - pr: ${{ github.event.number }} - invalid: ${{ fromJSON(steps.hash.outputs.json)[github.repository] }} - - build-md-source: - name: "Build markdown source files if valid" - needs: test-pr - runs-on: ubuntu-22.04 - if: ${{ needs.test-pr.outputs.is_valid == 'true' }} - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - RENV_PATHS_ROOT: ~/.local/share/renv/ - CHIVE: ${{ github.workspace }}/site/chive - PR: ${{ github.workspace }}/site/pr - MD: ${{ github.workspace }}/site/built - steps: - - name: "Check Out Main Branch" - uses: actions/checkout@v4 - - - name: "Check Out Staging Branch" - uses: actions/checkout@v4 - with: - ref: md-outputs - path: ${{ env.MD }} - - - name: "Set up R" - uses: r-lib/actions/setup-r@v2 - with: - use-public-rspm: true - install-r: false - - - name: "Set up Pandoc" - uses: r-lib/actions/setup-pandoc@v2 - - - name: "Setup Lesson Engine" - uses: carpentries/actions/setup-sandpaper@main - with: - cache-version: ${{ secrets.CACHE_VERSION }} - - - name: "Setup Package Cache" - uses: carpentries/actions/setup-lesson-deps@main - with: - cache-version: ${{ secrets.CACHE_VERSION }} - - - name: "Validate and Build Markdown" - id: build-site - run: | - sandpaper::package_cache_trigger(TRUE) - sandpaper::validate_lesson(path = '${{ github.workspace }}') - sandpaper:::build_markdown(path = '${{ github.workspace }}', quiet = FALSE) - shell: Rscript {0} - - - name: "Generate Artifacts" - id: generate-artifacts - run: | - sandpaper:::ci_bundle_pr_artifacts( - repo = '${{ github.repository }}', - pr_number = '${{ github.event.number }}', - path_md = '${{ env.MD }}', - path_pr = '${{ env.PR }}', - path_archive = '${{ env.CHIVE }}', - branch = 'md-outputs' - ) - shell: Rscript {0} - - - name: "Upload PR" - uses: actions/upload-artifact@v4 - with: - name: pr - path: ${{ env.PR }} - overwrite: true - - - name: "Upload Diff" - uses: actions/upload-artifact@v4 - with: - name: diff - path: ${{ env.CHIVE }} - retention-days: 1 - - - name: "Upload Build" - uses: actions/upload-artifact@v4 - with: - name: built - path: ${{ env.MD }} - retention-days: 1 - - - name: "Teardown" - run: sandpaper::reset_site() - shell: Rscript {0} diff --git a/.github/workflows/sandpaper-main.yaml b/.github/workflows/sandpaper-main.yaml deleted file mode 100755 index b3d1de8c8..000000000 --- a/.github/workflows/sandpaper-main.yaml +++ /dev/null @@ -1,64 +0,0 @@ -name: "01 Build and Deploy Site" - -on: - push: - branches: - - main - - master - schedule: - - cron: '0 0 * * 2' - workflow_dispatch: - inputs: - name: - description: 'Who triggered this build?' - required: true - default: 'Maintainer (via GitHub)' - reset: - description: 'Reset cached markdown files' - required: false - default: false - type: boolean -jobs: - full-build: - name: "Build Full Site" - - # 2024-10-01: ubuntu-latest is now 24.04 and R is not installed by default in the runner image - # pin to 22.04 for now - runs-on: ubuntu-22.04 - permissions: - checks: write - contents: write - pages: write - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - RENV_PATHS_ROOT: ~/.local/share/renv/ - steps: - - - name: "Checkout Lesson" - uses: actions/checkout@v4 - - - name: "Set up R" - uses: r-lib/actions/setup-r@v2 - with: - use-public-rspm: true - install-r: false - - - name: "Set up Pandoc" - uses: r-lib/actions/setup-pandoc@v2 - - - name: "Setup Lesson Engine" - uses: carpentries/actions/setup-sandpaper@main - with: - cache-version: ${{ secrets.CACHE_VERSION }} - - - name: "Setup Package Cache" - uses: carpentries/actions/setup-lesson-deps@main - with: - cache-version: ${{ secrets.CACHE_VERSION }} - - - name: "Deploy Site" - run: | - reset <- "${{ github.event.inputs.reset }}" == "true" - sandpaper::package_cache_trigger(TRUE) - sandpaper:::ci_deploy(reset = reset) - shell: Rscript {0} diff --git a/.github/workflows/sandpaper-version.txt b/.github/workflows/sandpaper-version.txt index 0cc988469..543466e4d 100644 --- a/.github/workflows/sandpaper-version.txt +++ b/.github/workflows/sandpaper-version.txt @@ -1 +1 @@ -0.18.4 +0.18.5 diff --git a/.github/workflows/update-cache.yaml b/.github/workflows/update-cache.yaml old mode 100755 new mode 100644 index 27b6d1cd9..ce318f6f1 --- a/.github/workflows/update-cache.yaml +++ b/.github/workflows/update-cache.yaml @@ -154,7 +154,7 @@ jobs: steps.update.outputs.n > 0 uses: carpentries/create-pull-request@main with: - token: ${{ steps.set-pat.outputs.pat || secrets.SANDPAPER_WORKFLOW || secrets.GITHUB_TOKEN }} + token: ${{ steps.set-pat.outputs.pat || secrets.SANDPAPER_WORKFLOW }} delete-branch: true branch: "update/packages" commit-message: "[actions] update ${{ steps.update.outputs.n }} packages" @@ -188,13 +188,3 @@ jobs: run: | echo "No updates needed, skipping PR creation" shell: bash - - # thanks @Bisaloo! - https://github.com/carpentries/sandpaper/issues/646#issuecomment-2829578435 - # only trigger checks manually if the validate-token step had no valid AWS or SANDPAPER_WORKFLOW token - - name: "Trigger checks" - if: | - steps.cpr.outputs.pull-request-number != '' && - steps.validate-org-workflow.outputs.is_valid != 'true' - run: | - gh workflow run docker_pr_receive.yaml --field pr_number=${{ steps.cpr.outputs.pull-request-number }} - shell: bash diff --git a/.github/workflows/update-workflows.yaml b/.github/workflows/update-workflows.yaml old mode 100755 new mode 100644 From 4781335f18b7308da9f7fa98b7c2dbc50d13b838 Mon Sep 17 00:00:00 2001 From: zkamvar <3639446+zkamvar@users.noreply.github.com> Date: Tue, 3 Mar 2026 00:06:43 +0000 Subject: [PATCH 30/33] [actions] update sandpaper workflow to version 1.0.0 --- .github/workflows/README.md | 280 +++++++++------------ .github/workflows/docker_build_deploy.yaml | 2 + .github/workflows/sandpaper-version.txt | 1 - .github/workflows/update-workflows.yaml | 9 +- .github/workflows/workflows-version.txt | 1 + 5 files changed, 122 insertions(+), 171 deletions(-) delete mode 100644 .github/workflows/sandpaper-version.txt create mode 100644 .github/workflows/workflows-version.txt diff --git a/.github/workflows/README.md b/.github/workflows/README.md index a57a31c09..59a486100 100755 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -1,21 +1,6 @@ -# Carpentries Workflows +# Workflow Documentation -This directory contains workflows to be used for Lessons using the Carpentries Workbench lesson infrastructure. - -The three `docker-` workflows build lessons and maintain packages. -The workflows run using the [workbench-docker](https://github.com/carpentries/workbench-docker) container. -This container comprises prebuilt and installed dependencies of the core Workbench packages, i.e. sandpaper, pegboard and varnish. - -Two `update-` workflows handle: - - checking for new renv packages and creating a Pull Request (PR) when a renv.lock file updates (`update-cache.yaml`) - - checking for updated versions of these workflow files (`update-workflows.yaml`) - -The rest of the `pr-` workflows handle pull request management via base GitHub Actions. - -For Carpentries Core Curriculum lessons across our lesson programmes, maintenance of these workflows should be minimal. -For your own lesson repositories, it is important to understand the different workflows and what they do. - -## Managing Updates +## Managing Workflow Updates By using prebuilt Docker containers that are managed by the Carpentries core Workbench maintainers, these workflows are designed to be rarely updated. @@ -47,19 +32,30 @@ $ git commit -m "Manual update to docker workflows" $ git push origin main ``` -This will automatically start the "01 Maintain: Build and Deploy Site" workflow. +> [!NOTE] +> For non-renv lessons, this is all the setup you need! +> +> For renv-enabled lessons: +> - Cancel any "01 Maintain: Build and Deploy Site" workflow currently running +> - Run the "02 Maintain: Check for Updated Packages" workflow and merge any PR opened to update the renv lockfile +> - This should automatically run the "03 Maintain: Apply Package Cache" workflow to install packages and build the cache +> - A successful cache buid should then trigger the "01 Maintain: Build and Deploy Site" workflow -This will be the extent of requirements for non-renv lessons. +### Updating using GitHub -#### Lessons that use Rmd and {renv} +#### Official lessons -For renv-enabled lessons: -- Cancel the "01 Maintain: Build and Deploy Site" run that automatically started following the push to main -- Run the "02 Maintain: Check for Updated Packages" -- Run the "03 Maintain: Apply Package Cache" -- Run the "01 Maintain: Build and Deploy Site" +"Official" lessons are those in the lesson program repositories, Incubator, or Lab. +They need no extra setup as this is all managed for you as part of the Carpentries GitHub organisations. -### Updating using GitHub +To update the workflows, either: +- wait for the scheduled run of the "04 Maintain: Update Workflow Files" at approximately midnight every Tuesday +- go to the Actions tab on GitHub, click "04 Maintain: Update Workflow Files" on the left, then "Run Workflow" on the right + +Once complete, this will raise a PR with any changes to the workflows that are needed. +If you are happy with the changes made, you can merge the PR into your lesson repository. + +#### Your own lessons This presumes you: - already have a lesson repository available on GitHub @@ -74,34 +70,19 @@ Once set up, run the "04 Maintain: Update Workflow Files" (`update-workflows.yam This will raise a PR with any changes to the workflows that are needed. If you are happy with the changes made, you can merge the PR into your lesson repository. -## Lesson Builds and Deployment - -### 01 Maintain: Build and Deploy Site (docker_build_deploy.yaml) - -This is the main workflow that you will encounter most often. - -It will only act on the main branch of the lesson repository. - -This workflow does the following: - 1. checks out the lesson - 2. provisions the following resources - - the Workbench Docker container - - lesson dependencies if needed (stored in a cache) - 3. builds the lesson via `sandpaper:::ci_deploy()` -If your lesson contains rendered content using RMarkdown and/or any associated R package dependencies, you will need to generate and apply the renv cache. -Please read the [Caching](#caching) section below. +## Package Caches for RMarkdown Lessons -#### Caching +In summary, generating a reusable package cache is achieved by running the "02 Maintain: Check for Updated Packages" workflow, and then the "03 Maintain: Apply Package Cache" workflow. > [!NOTE] > Caching is only relevant for lessons that use Rmd files and renv to manage R packages. > If you are building basic markdown documents, caching will not apply to you, and the only > workflow that needs to be run is "01 Maintain: Build and Deploy Site". -In summary, generating a reusable package cache is achieved by running the "02 Maintain: Check for Updated Packages" workflow, and then the "03 Maintain: Apply Package Cache" workflow. +### Caching -These workflows are separated to ensure that once you have a successful build with a working renv cache, this cache is stored within GitHub's infrastructure, and will be reused by the Workbench Docker container. +The two cache management workflows are separated to ensure that once you have a successful build with a working renv cache, this cache is stored and will be reused by the Workbench Docker container. This means that lesson builds will be faster once an renv cache is created and reused by the Docker container. Another major bonus of this setup is that you can keep using this cache indefinitely to build your lesson. @@ -123,18 +104,14 @@ There are times when you may want to go back to a previous renv package cache fi - if you run "02 Maintain: Check for Updated Packages" and "03 Maintain: Apply Package Cache" and the cache generation fails for some reason - if there is a new R package that produces incorrect or broken lesson output -To choose a previous cache file version for your builds, go to the Actions tab, and click Caches in the left hand pane. - -Cache files should have the following name format: +Cache files will have the following name format, where IMAGE is the workbench-docker image version, and HASHSUM is the `renv.lock` lockfile MD5 hash: ``` - OS HASHSUM -[ | ] [ | ] -Linux--renv-2e499eb706112971b2cffceb49b55a6efe49f3ed75cd6579b10ff224489daca4 +IMAGE HASHSUM +[ | ] [ | ] +v0.2.4_renv-2e499eb706112971b2cffceb49b55a6efe49f3ed75cd6579b10ff224489daca4 ``` -Once you have 2 or more cache files, you can choose which one you want to use. - Copy the hashsum part of the desired cache file you want to use, e.g. `2e499eb706112971b2cffceb49b55a6efe49f3ed75cd6579b10ff224489daca4`. Then either: @@ -145,137 +122,112 @@ Then either: If you have no caches listed, make sure to run the "02 Maintain: Check for Updated Packages" and "03 Maintain: Apply Package Cache" to create a new renv cache file. -## Updates - -### Setup Information - -These workflows run on a mix of schedules, automatic triggers, and at the maintainer's request. -Because they create pull requests that update workflows/require the downstream actions to run, -they need a special repository/organization secret token called -`SANDPAPER_WORKFLOW` and it must have the `public_repo` and `workflow` scope. - -This can be an individual user token, OR it can be a trusted bot account. If you -have a repository in one of the official Carpentries organisations, then you do not -need to worry about this token being present because the Carpentries Core Team -will take care of supplying this token. - -If you want to use your personal account: you can go to - -to create a token. Once you have created your token, you should copy it to your -clipboard and then go to your repository's settings > secrets > actions and -create or edit the `SANDPAPER_WORKFLOW` secret, pasting in the generated token. - -If you do not specify your token correctly, the runs will not fail and they will -give you instructions to provide the token for your repository. +> [!NOTE] +> If you are maintaining an official lesson, caches are saved in an AWS S3 bucket owned by the Carpentries. +> Once a successful cache has been saved, these will be listed in the outputs of the "01 Maintain: Build and Deploy Site" workflow. +> +> If you are developing a lesson in your own repository, caches are saved on GitHub. +> You can see available caches by going to the Actions tab, and clicking Caches on the left hand side. -### "02 Maintain: Check for Updated Packages" (update-cache.yaml) -For lessons that have generated content, we use {renv} to ensure that the output -is stable. This is controlled by a single lockfile which documents the packages -needed for the lesson and the version numbers. This workflow is skipped in -lessons that do not have generated content. +## User Settings -Packages are frequently updated, fixing bugs or introducing new features. It's a -good idea to make sure these packages can be both: updated periodically, or; or left -static to ensure consistent lesson builds. +Input level variables are documented in the `carpentries/actions` repository READMEs for each composite action. -The update cache workflow will do this by: -- checking repositories for updates -- updating the renv lockfile -- summarising the updated packages and their versions in a branch called `updates/packages` -- creating a pull request with _only the renv lockfile changed_ +Specific repository level variables can be set that will force particular options across all workflow runs. -From here, the markdown documents will be rebuilt and you can inspect what has -changed based on how the packages have updated. +### 01 Maintain: Build and Deploy Site (docker_build_deploy.yaml) -If all steps pass in this workflow, you can safely merge the PR that is raised. -Once the PR is merged, the "03 Maintain: Apply Package Cache" workflow will run -automatically. +Repository-level variables for this workflow are: +- WORKBENCH_TAG + - The workbench-docker release version to use for a given build + - This can be set to a specific version number to force all builds to use a given container version + - Default is unset or `latest` +- BUILD_RESET + - Force a reset of previously build markdown files + - Setting this variable value to `true` will force sandpaper to delete any previously build markdown files + - Default is unset or `false` +- AUTO_MERGE_WORKBENCH_VERSION_UPDATE + - Control merge behaviour of the workbench-docker version update PR + - When a new workbench Docker image version is detected, usually after a sandpaper, varnish, or pegboard update, its version number will be incremented + - If a newer version is available, a PR will be auto-generated that updates the `.github/workbench-docker-version.txt` file, and this PR will be auto-merged + - To not auto-merge this PR and to choose when to update the Docker version used, set this to `false`. + - Default is unset or `true` +- LANG_CODE + - Two-letter language code that triggers the use of Joel Nitta's {dovetail} package for lesson translation + - This is used in the internationalisation repos of the main Carpentry lesson programs + - Default is unset or `''` + +### 02 Maintain: Check for Updated Packages (update-cache.yaml) + +Repository-level variables for this workflow are: +- LOCKFILE_CACHE_GEN + - Passed to the `generate-cache` input of the [update-lockfile](https://github.com/carpentries/actions/tree/main/update-lockfile) action + - A temporary renv cache is generated when this workflow runs + - If this option is set to `false`, no temporary cache will be generated + - Default is `true` +- FORCE_RENV_INIT + - Passed to the `force-renv-init` input of the [update-lockfile](https://github.com/carpentries/actions/tree/main/update-lockfile) action + - renv initialises a cache based on a given lockfile + - If this lockfile is particularly old or packages have broken/unresolvable dependencies, then builds will fail + - If this option is set to `true`, a full renv reinitialisation will occur, "wiping the slate clean" + - This option is useful if you're using Bioconductor packages which often break when new Bioconductor releases happen + - Default is `false` +- UPDATE_PACKAGES + - Passed to the `update` input of the [update-lockfile](https://github.com/carpentries/actions/tree/main/update-lockfile) action + - If set to `false` only package hydration will happen and no package update checks will occur + - Default is `true` ### 03 Maintain: Apply Package Cache (docker_apply_cache.yaml) -This workflow takes the updated lockfile produced in "02 Maintain: Check for Updated Packages" -and uses it to produce a cached file stored within GitHub's infrastructure. - -This cached file can then be reused repeatedly by the "01 Maintain: Build and Deploy Site" -workflow. - -This workflow is run automatically when the PR generated by "02 Maintain: Check for Updated Packages" -is closed and merged. - -You would only ever need to run this workflow manually: -- if your cache gets removed by GitHub due to age or non-use -- if your cache file contains packages that cannot be used by a Workbench Docker container's newer R version - -### "04 Maintain: Update Workflow Files" (update-workflows.yaml) - -The {sandpaper} repository was designed to do as much as possible to separate -the tools from the content. For local builds, this is absolutely true as you -can develop and build lessons without any GitHub workflows. When it comes to -workflow files on GitHub itself for managed builds online, the workflows must -live inside the lesson repository. +Repository-level variables for this workflow are: +- WORKBENCH_TAG + - The workbench-docker release version to use for a given build + - This can be set to a specific version number to force all builds to use a given container version + - Default is unset or `latest` -This workflow ensures that the workflow files are up-to-date. It downloads the -`update-workflows.sh` script from GitHub and runs it. The script will do the -following: -1. check the recorded version of sandpaper against the current version on GitHub -2. update the files if there is a difference in versions +### 04 Maintain: Update Workflow Files (update-workflows.yaml) -After the files are updated, and if there are any changes, they are pushed to a -branch called `update/workflows` and a pull request is created. Maintainers are -encouraged to review the changes and accept the pull request if the outputs -are okay. +There are no repository variables for this workflow. -This update is run weekly or on demand. ## Pull Request and Review Management -Because our lessons execute code, pull requests are a secruity risk for any -lesson and thus have security measures associted with them. **Do not merge any -pull requests that do not pass checks and do not have bots commented on them.** +Because our lessons execute code, pull requests are a security risk for any lesson and thus have security measures associted with them. +**Do not merge any pull requests that do not pass checks and do not have bots commented on them.** -This series of workflows all go together and are described in the following -diagram and the below sections: +This series of workflows all go together and are described in the following diagram and the below sections: ![Graph representation of a pull request](https://carpentries.github.io/sandpaper/articles/img/pr-flow.dot.svg) ### Pre Flight Pull Request Validation (pr-preflight.yaml) -This workflow runs every time a pull request is created and its purpose is to -validate that the pull request is okay to run. This means the following things: +This workflow runs every time a pull request is created and its purpose is to validate that the pull request is okay to run. +This means the following things: 1. The pull request does not contain modified workflow files -2. If the pull request contains modified workflow files, it does not contain - modified content files (such as a situation where @carpentries-bot will - make an automated pull request) -3. The pull request does not contain an invalid commit hash (e.g. from a fork - that was made before a lesson was transitioned from styles to use the - workbench). +2. If the pull request contains modified workflow files, it does not contain modified content files + (such as a situation where @carpentries-bot will make an automated pull request) +3. The pull request does not contain an invalid commit hash + (e.g. from a fork that was made before a lesson was transitioned from styles to use the Workbench). -Once the checks are finished, a comment is issued to the pull request, which -will allow maintainers to determine if it is safe to run the -"Receive Pull Request" workflow from new contributors. +Once the checks are finished, a comment is issued to the pull request, which will allow maintainers to determine if it is safe to run the "Receive Pull Request" workflow from new contributors. ### Receive Pull Request (docker_pr_receive.yaml) -**Note of caution:** This workflow runs arbitrary code by anyone who creates a -pull request. GitHub has safeguarded the token used in this workflow to have no -privileges in the repository, but we have taken precautions to protect against -spoofing. +**Note of caution:** This workflow runs arbitrary code by anyone who creates a pull request. +GitHub has safeguarded the token used in this workflow to have no privileges in the repository, but we have taken precautions to protect against spoofing. -This workflow is triggered with every push to a pull request. If this workflow -is already running and a new push is sent to the pull request, the workflow -running from the previous push will be cancelled and a new workflow run will be -started. +This workflow is triggered with every push to a pull request. +If this workflow is already running and a new push is sent to the pull request, the workflow running from the previous push will be cancelled and a new workflow run will be started. -The first step of this workflow is to check if it is valid (e.g. that no -workflow files have been modified). If there are workflow files that have been -modified, a comment is made that indicates that the workflow is not run. If -both a workflow file and lesson content is modified, an error will occurr. +The first step of this workflow is to check if it is valid (e.g. that no workflow files have been modified): +- If there are workflow files that have been modified, a comment is made that indicates that the workflow will not continue. +- If both a workflow file and lesson content is modified, an error will occur and the workflow will not continue. -The second step (if valid) is to build the generated content from the pull -request. This builds the content and uploads three artifacts: +The second step (if valid) is to build the generated content from the pull request. +This builds the content and uploads three artifacts: 1. The pull request number (pr) 2. A summary of changes after the rendering process (diff) @@ -288,25 +240,21 @@ The artifacts produced are used by the "Comment on Pull Request" workflow. This workflow is triggered if the `docker_pr_receive.yaml` workflow is successful. The steps in this workflow are: -1. Test if the workflow is valid and comment the validity of the workflow to the - pull request. -2. If it is valid: create an orphan branch with two commits: the current state - of the repository and the proposed changes. +1. Test if the workflow is valid and comment the validity of the workflow to the pull request. +2. If it is valid: create an orphan branch with two commits: the current state of the repository and the proposed changes. 3. If it is valid: update the pull request comment with the summary of changes -Importantly: if the pull request is invalid, the branch is not created so any -malicious code is not published. +Importantly: if the pull request is invalid, the branch is not created so any malicious code is not published. -From here, the maintainer can request changes from the author and eventually -either merge or reject the PR. When this happens, if the PR was valid, the -preview branch needs to be deleted. +From here, the maintainer can request changes from the author and eventually either merge or reject the PR. +When this happens, if the PR was valid, the preview branch needs to be deleted. ### Send Close PR Signal (pr-close-signal.yaml) -Triggered any time a pull request is closed. This emits an artifact that is the -pull request number for the next action +Triggered any time a pull request is closed. +This emits an artifact that is the pull request number for the next action. ### Remove Pull Request Branch (pr-post-remove-branch.yaml) -Tiggered by `pr-close-signal.yaml`. This removes the temporary branch associated with -the pull request (if it was created). +Tiggered by `pr-close-signal.yaml`. +This removes the temporary branch associated with the pull request (if it was created). diff --git a/.github/workflows/docker_build_deploy.yaml b/.github/workflows/docker_build_deploy.yaml index df3e8d1b8..017192061 100644 --- a/.github/workflows/docker_build_deploy.yaml +++ b/.github/workflows/docker_build_deploy.yaml @@ -4,6 +4,7 @@ on: push: branches: - 'main' + - 'l10n_main' paths-ignore: - '.github/workflows/**.yaml' - '.github/workbench-docker-version.txt' @@ -132,6 +133,7 @@ jobs: with: reset: ${{ vars.BUILD_RESET || github.event.inputs.reset || 'false' }} skip-manage-deps: ${{ github.event.inputs.force-skip-manage-deps == 'true' || steps.build-container-deps.outputs.renv-cache-available || steps.build-container-deps.outputs.backup-cache-used || 'false' }} + lang-code: ${{ vars.LANG_CODE || '' }} update-container-version: name: "Update container version used" diff --git a/.github/workflows/sandpaper-version.txt b/.github/workflows/sandpaper-version.txt deleted file mode 100644 index 543466e4d..000000000 --- a/.github/workflows/sandpaper-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.18.5 diff --git a/.github/workflows/update-workflows.yaml b/.github/workflows/update-workflows.yaml index 09ec1b638..e57927bb5 100644 --- a/.github/workflows/update-workflows.yaml +++ b/.github/workflows/update-workflows.yaml @@ -1,5 +1,6 @@ name: "04 Maintain: Update Workflow Files" description: "Update workflow files from the carpentries/sandpaper repository" + on: schedule: - cron: '0 0 * * 2' @@ -9,10 +10,10 @@ on: description: 'Who triggered this build (enter github username to tag yourself)?' required: true default: 'weekly run' - tarball: - description: 'Absolute URL to the desired sandpaper repo tarball' + version: + description: 'Workflows version number (e.g. 0.0.1), branch name (e.g. main), or "latest"' required: false - default: '' + default: 'latest' clean: description: 'Workflow files/file extensions to clean (no wildcards, enter "" for none)' required: false @@ -88,7 +89,7 @@ jobs: if: ${{ steps.validate-token.outputs.wf == 'true' }} uses: carpentries/actions/update-workflows@main with: - repo: ${{ github.event.inputs.tarball || 'https://carpentries.r-universe.dev' }} + version: ${{ github.event.inputs.version || 'latest' }} clean: ${{ github.event.inputs.clean || '.yaml' }} - name: Create Pull Request diff --git a/.github/workflows/workflows-version.txt b/.github/workflows/workflows-version.txt new file mode 100644 index 000000000..3eefcb9dd --- /dev/null +++ b/.github/workflows/workflows-version.txt @@ -0,0 +1 @@ +1.0.0 From fd667dcb9d34b32de63b65864eff0af4c90ebc72 Mon Sep 17 00:00:00 2001 From: zkamvar <3639446+zkamvar@users.noreply.github.com> Date: Tue, 21 Apr 2026 00:13:40 +0000 Subject: [PATCH 31/33] [actions] update sandpaper workflow to version 1.0.1 --- .github/workflows/docker_apply_cache.yaml | 8 ++++---- .github/workflows/docker_build_deploy.yaml | 4 ++-- .github/workflows/docker_pr_receive.yaml | 20 ++++++++++---------- .github/workflows/pr-close-signal.yaml | 2 +- .github/workflows/pr-comment.yaml | 4 ++-- .github/workflows/update-cache.yaml | 6 +++--- .github/workflows/update-workflows.yaml | 4 ++-- .github/workflows/workflows-version.txt | 2 +- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/workflows/docker_apply_cache.yaml b/.github/workflows/docker_apply_cache.yaml index 2c3a3bce2..0f3a1abb9 100644 --- a/.github/workflows/docker_apply_cache.yaml +++ b/.github/workflows/docker_apply_cache.yaml @@ -132,7 +132,7 @@ jobs: - ${{ github.workspace }}:/home/rstudio/lesson options: --cpus 2 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: "Debugging Info" run: | @@ -187,7 +187,7 @@ jobs: steps.validate-org-workflow.outputs.is_valid == 'true' && env.role-to-assume != '' && env.aws-region != '' - uses: aws-actions/configure-aws-credentials@v5.0.0 + uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ env.role-to-assume }} aws-region: ${{ env.aws-region }} @@ -195,7 +195,7 @@ jobs: - name: "Upload cache object to S3" id: upload-cache - uses: carpentries/actions-cache@frog-matchedkey-1 + uses: tespkg/actions-cache@v1.10.0 with: accessKey: ${{ steps.aws-creds.outputs.aws-access-key-id }} secretKey: ${{ steps.aws-creds.outputs.aws-secret-access-key }} @@ -223,7 +223,7 @@ jobs: shell: bash - name: "Upload cache result" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: apply-cache-result path: ${{ github.workspace }}/apply-cache-result diff --git a/.github/workflows/docker_build_deploy.yaml b/.github/workflows/docker_build_deploy.yaml index 017192061..4baf306f9 100644 --- a/.github/workflows/docker_build_deploy.yaml +++ b/.github/workflows/docker_build_deploy.yaml @@ -61,7 +61,7 @@ jobs: - name: "Checkout Lesson" if: steps.build-check.outputs.do-build == 'true' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: "Get container version info" id: wb-vers @@ -99,7 +99,7 @@ jobs: - ${{ github.workspace }}:/home/rstudio/lesson options: --cpus 1 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: "Debugging Info" run: | diff --git a/.github/workflows/docker_pr_receive.yaml b/.github/workflows/docker_pr_receive.yaml index 3d01d9dc5..486b4b4fb 100644 --- a/.github/workflows/docker_pr_receive.yaml +++ b/.github/workflows/docker_pr_receive.yaml @@ -26,7 +26,7 @@ jobs: branch-exists: ${{ steps.check.outputs.exists }} steps: - name: "Checkout Lesson" - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: "Check if md-outputs branch exists" id: check @@ -76,7 +76,7 @@ jobs: - name: "Upload PR number" id: upload if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: pr path: ${{ github.workspace }}/NR @@ -109,7 +109,7 @@ jobs: renv-cache-hashsum: ${{ steps.renv-check.outputs.renv-cache-hashsum }} steps: - name: "Checkout Lesson" - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: "Is renv required?" id: renv-check @@ -153,10 +153,10 @@ jobs: workbench-update: ${{ steps.wb-vers.outputs.workbench-update }} build-site: ${{ steps.build-site.outcome }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: "Check Out Staging Branch" - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: md-outputs path: ${{ env.GHWMD }} @@ -205,7 +205,7 @@ jobs: needs.check-renv.outputs.renv-needed == 'true' && env.role-to-assume != '' && env.aws-region != '' - uses: aws-actions/configure-aws-credentials@v5.0.0 + uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ env.role-to-assume }} aws-region: ${{ env.aws-region }} @@ -213,7 +213,7 @@ jobs: - name: Get cache object from S3 id: s3-cache - uses: carpentries/actions-cache/restore@frog-matchedkey-1 + uses: tespkg/actions-cache/restore@v1.10.0 if: needs.check-renv.outputs.renv-needed == 'true' with: # insecure: false # optional, use http instead of https. default false @@ -258,21 +258,21 @@ jobs: shell: Rscript {0} - name: "Upload PR" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: pr path: ${{ env.PR }} overwrite: true - name: "Upload Diff" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: diff path: ${{ env.CHIVE }} retention-days: 1 - name: "Upload Build" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: built path: ${{ env.GHWMD }} diff --git a/.github/workflows/pr-close-signal.yaml b/.github/workflows/pr-close-signal.yaml index b1303c261..de1f25448 100644 --- a/.github/workflows/pr-close-signal.yaml +++ b/.github/workflows/pr-close-signal.yaml @@ -16,7 +16,7 @@ jobs: mkdir -p ./pr printf ${{ github.event.number }} > ./pr/NUM - name: Upload Diff - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: pr path: ./pr diff --git a/.github/workflows/pr-comment.yaml b/.github/workflows/pr-comment.yaml index 7614abd03..9ec78c6c8 100644 --- a/.github/workflows/pr-comment.yaml +++ b/.github/workflows/pr-comment.yaml @@ -42,7 +42,7 @@ jobs: exit 1 - name: "Checkout Lesson" - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: "Verify committed files" id: changed-files @@ -112,7 +112,7 @@ jobs: contents: write steps: - name: "Checkout md outputs" - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: md-outputs path: built diff --git a/.github/workflows/update-cache.yaml b/.github/workflows/update-cache.yaml index ce318f6f1..d182ac7a0 100644 --- a/.github/workflows/update-cache.yaml +++ b/.github/workflows/update-cache.yaml @@ -63,7 +63,7 @@ jobs: renv-needed: ${{ steps.renv-check.outputs.renv-needed }} steps: - name: "Checkout Lesson" - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: "Is renv required?" id: renv-check @@ -89,7 +89,7 @@ jobs: RENV_PATHS_ROOT: ~/.local/share/renv/ steps: - name: "Checkout Lesson" - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: "Set up R" uses: r-lib/actions/setup-r@v2 @@ -121,7 +121,7 @@ jobs: steps.validate-org-workflow.outputs.is_valid == 'true' && env.role-to-assume != '' && env.aws-region != '' - uses: aws-actions/configure-aws-credentials@v5.0.0 + uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ env.role-to-assume }} aws-region: ${{ env.aws-region }} diff --git a/.github/workflows/update-workflows.yaml b/.github/workflows/update-workflows.yaml index e57927bb5..35106872c 100644 --- a/.github/workflows/update-workflows.yaml +++ b/.github/workflows/update-workflows.yaml @@ -29,7 +29,7 @@ jobs: id-token: write steps: - name: "Checkout Repository" - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: "Validate Current Org and Workflow" id: validate-org-workflow @@ -46,7 +46,7 @@ jobs: steps.validate-org-workflow.outputs.is_valid == 'true' && env.role-to-assume != '' && env.aws-region != '' - uses: aws-actions/configure-aws-credentials@v5.0.0 + uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ env.role-to-assume }} aws-region: ${{ env.aws-region }} diff --git a/.github/workflows/workflows-version.txt b/.github/workflows/workflows-version.txt index 3eefcb9dd..7dea76edb 100644 --- a/.github/workflows/workflows-version.txt +++ b/.github/workflows/workflows-version.txt @@ -1 +1 @@ -1.0.0 +1.0.1 From 5d886a81763f0c113f5560368311c44f41eea02d Mon Sep 17 00:00:00 2001 From: "The Carpentries Apprentice (beta)" <64428345+carpentries-bot@users.noreply.github.com> Date: Mon, 25 May 2026 21:09:00 -0400 Subject: [PATCH 32/33] [actions] update workbench docker version to v0.2.7 (#717) Co-authored-by: alee <22534+alee@users.noreply.github.com> --- .github/workbench-docker-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workbench-docker-version.txt b/.github/workbench-docker-version.txt index f82e0685d..34707cbb1 100644 --- a/.github/workbench-docker-version.txt +++ b/.github/workbench-docker-version.txt @@ -1 +1 @@ -v0.2.4 +v0.2.7 From 2039b8ac7f68ad5225a2dfa6909d2a522ee2f44d Mon Sep 17 00:00:00 2001 From: "The Carpentries Apprentice (beta)" <64428345+carpentries-bot@users.noreply.github.com> Date: Tue, 1 Sep 2026 02:01:24 +0100 Subject: [PATCH 33/33] [actions] update workbench docker version to v0.2.8 (#725) Co-authored-by: alee <22534+alee@users.noreply.github.com> --- .github/workbench-docker-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workbench-docker-version.txt b/.github/workbench-docker-version.txt index 34707cbb1..d2db7dbfe 100644 --- a/.github/workbench-docker-version.txt +++ b/.github/workbench-docker-version.txt @@ -1 +1 @@ -v0.2.7 +v0.2.8